[
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.yml",
    "content": "name: \"Bug report\"\ndescription: Report problematic behaviour.\nbody:\n- type: markdown\n  attributes:\n    value: |\n      If this is a new feature request please use the RetroNAS Discussion area. \n      \n      Before raising an issue confirm you are using a supported distribution by checking the wiki and include as much detail as possible about your issue\n- type: textarea\n  attributes:\n    label: Details\n    description: |\n      Any relevant details, e.g Error message text or screenshot, retronas branch, linux distribution information, ansible version etc.\n    placeholder: |\n      Installer <RETRONAS INSTALLER ended in with failure message <MESSAGE>, on retronas <BRANCH>. \n      \n      I am using a supported distribution <DISTRO> <VERSION> with Ansible <VERSION> installed."
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: true\ncontact_links:\n  - name: Discussions\n    url: https://github.com/orgs/retronas/discussions\n    about: For questions, feature requests etc.\n  - name: \"#retronas\"\n    url: https://libera.chat/guides/connect\n    about: General chatter on Libera IRC\n  - name: r/retroNAS\n    url: https://www.reddit.com/r/retroNAS/\n    about: Community support (not monitored)"
  },
  {
    "path": ".gitignore",
    "content": "bin\nscripts/*.sh\n!scripts/static/*.sh\nsrc\nlog\ndoc\nansible/retronas_vars.yml\netc/*\nconfig/installed.json\nansible/collections\n\n.fuse*\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 Dan Mons\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\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"
  },
  {
    "path": "README.md",
    "content": "![logo](dist/retronas-logo.png)\n# RetroNAS\n\n## Project Information\n* [Status](https://github.com/retronas/retronas/wiki/Status)\n* [About](https://github.com/retronas/retronas/wiki/About)\n* [HowTo](https://github.com/retronas/retronas/wiki)\n* [Contributing](https://github.com/retronas/retronas/wiki/Contributing)\n* [Thanks and Credits](https://github.com/retronas/retronas/wiki/Credits)\n\n## WARNINGS\n* [SECURITY](https://github.com/retronas/retronas/wiki/SECURITY-WARNING)\n* [FILENAMES](https://github.com/retronas/retronas/wiki/Filenames)\n\n## Community\n* [Guides](https://github.com/retronas/retronas/wiki/Guides)\n* [Coverage](https://github.com/retronas/retronas/wiki/Coverage)\n* [Other Projects](https://github.com/retronas/retronas/wiki/Other-projects-and-sites)\n"
  },
  {
    "path": "SECURITY",
    "content": "=============================================================================\n                    WARNING / TERMS OF USE / LICENSE\n=============================================================================\n\n          RetroNAS is a compilation of existing opensource products. \n\n              RetroNAS is made available under the MIT license\n            https://github.com/retronas/retronas/blob/main/LICENSE\n\nDue to the nature of retro computing, many of the tools and protocols used in \nthis project are COMPLETELY INSECURE. It is at the users behest which tools\nare installed and as such all liabilies are transfered to you as the user in \nthe use of RetroNAS.\n\nMost of these tools offer little to NO ENCRYPTION of neither data nor passwords, \nsome tools offer access to your system WITHOUT any AUTHENTICATION, and some of \nthe protocols have known EXPLOITS that cannot be fixed due to their legacy \ndesign. It is your responsiblity to secure your infrastructure.\n\n  The RetroNAS team only support use of this software on a PRIVATE NETWORK\n\nA private network is considered one where at the very least the RetroNAS is \nlocated behind a firewall that denies inbound traffic from the internet.\n\n         DO NOT expose a configured RetroNAS to the public INTERNET\n\nDoing so may result is unintentional DATA LOSS and or exposure of your network\nand subsequent contents beit computer and/or data to BAD ACTORS. It is your \nresponsiblity to secure your infrastructure.\n\nThe services RetroNAS installs will attempt to run as unprivileged user accounts \nwhere possible, but the RetroNAS installer scripts themselves all run as root\n(the \"Adminstrator\" account in Linux). These have the power to dramatically \nchange and break working systems, so please ensure you undeerstand the consequence\nof the code you are about to run, i.e code review etc but the onus is on you.\nRetroNAS provides automatic compilation, installation and configuration of many\nthird party tools these tools may be vulnerable to supply chain attacks of which \nwe have no control over at installation time. If a project is shown to have been \nexploited we may remove or modify installation support at any time. It is your\nresponsibility to ensure the options you are selecting for installation are as \nper the third party project authors intent and the onus is on said authors to \nsecure their products.\n\n       If you are UNSURE if ANY of the ABOVE statements apply to you. \n\n                    DO NOT CONTINUE to use this project.\n                    \n      If you have questions in regards to RetroNAS use and/or risk you may \n       ask your question on the github discussion boards for the project.\n\n       The RetroNAS team may in turn use this enquiry to revise support \n                 documentation for the benefit of the project\n\n                     https://github.com/retronas/retronas\n\n              The RetroNAS team are retro computing ENTHUSIASTS \n\nThe RetroNAS team should not be considered computer, networking nor security \nexperts and as such you are responsible to EDUCATE YOURSELF to the use of \nand/or risks associated with the tools in this project.\n\n          This is NOT A NAS project for use with modern computing\n\nIf you understand the above fully and accept all subsequent liabilties as your\nown in use of this project type AGREE at the prompt otherwise cease use of this\nproject immediately."
  },
  {
    "path": "ansible/ansible.cfg",
    "content": "[defaults]\ndeprecation_warnings = False\ncommand_warnings = False\ninventory = /opt/retronas/ansible/hosts.yml\nlog_path = /opt/retronas/log/ansible.log\ncallbacks_enabled = ansible.posix.profile_tasks,ansible.posix.profile_roles\ncallbacks_whitelist = ansible.posix.profile_tasks,ansible.posix.profile_roles\ncache = True\ngathering = smart\nfact_caching=jsonfile\nfact_caching_connection=/opt/retronas/cache\nfact_caching_timeout=86400\ncollections_paths = /opt/retronas/ansible/collections\nroles_path = /opt/retronas/ansible/roles\nprivate_role_vars = True\n"
  },
  {
    "path": "ansible/hosts.yml",
    "content": "---\nall:\n  hosts:\n    localhost:\n  vars:\n    ansible_connection: local\n    cache_plugin: jsonfile\n"
  },
  {
    "path": "ansible/install_3ds_qr_codes.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"3DS QR Codes\"\n    my_file: \"install_3ds_qr_codes\"\n    rom_path: \"{{ retronas_path }}/3ds\"\n    module_name: \"3ds_qr_codes\"\n\n    dirs:\n      - cia\n      - qr\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.nginx\n\n    - name: \"{{ my_name }} - configure 3DS directory\"\n      ansible.builtin.file:\n        path: \"{{ rom_path }}\"\n        owner: \"{{ retronas_user }}\"\n        group: \"{{ retronas_group }}\"\n        state: directory\n\n    - name: \"{{ my_name }} - create dirs\"\n      ansible.builtin.file:\n        path: \"{{ rom_path }}/{{ item }}\"\n        owner: \"{{ retronas_user }}\"\n        group: \"{{ retronas_group }}\"\n        state: directory\n      with_items: \"{{ dirs }}\"\n\n    - name: \"{{ my_name }} - link 3DS cia directory\"\n      ansible.builtin.file:\n        src: \"{{ rom_path }}/cia\"\n        dest: \"{{ retronas_path }}/roms/nintendo/3ds/cia\"\n        state: link\n\n    - name: \"{{ my_name }} - install qrencode\"\n      ansible.builtin.apt:\n        name: qrencode\n        state: latest\n\n    - name: \"{{ my_name }} - configure QR generator script\"\n      ansible.builtin.template:\n        src: \"templates/{{ my_file }}/3ds_qr.sh.j2\"\n        dest: \"{{ retronas_root }}/scripts/3ds_qr.sh\"\n        owner: root\n        group: root\n        mode: '0755'\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_adtpro.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"adtpro\"\n    my_file: \"install_{{ my_name }}\"\n    my_dir: /opt/adtpro\n    module_name: \"adtpro\"\n\n    systemd_units:\n      - { name: \"adtpro\", type: 'service', state: \"stopped\", enabled: \"no\", restart: \"no\", instance: \"no\" }\n\n    packages:\n      - default-jre\n      - xvfb\n      - x11vnc\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"ADTPro.properties\", dest: \"{{ my_dir }}\", force: no }\n      - { name: \"adtpro_retronas.sh\", dest: \"{{ my_dir }}\",  mode: \"0755\" }\n\n    paths:\n      - { name: \"{{ my_name }}\", dest: \"/opt\",                state: \"directory\", mode: \"0755\"}\n      # - { name: \"{{ my_name }}\", dest: \"{{ retronas_path }}\", state: \"directory\", mode: \"0755\"}\n\n    firewalld_ports:\n      - { port: 60000, protocol: tcp }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Include systems map\"\n      ansible.builtin.include_vars: \"retronas_systems.yml\"\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.x11vnc\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - disks symlink\"\n      ansible.builtin.file:\n        src: \"roms/apple/appleii\"\n        dest: \"{{ retronas_path }}/{{ my_name }}\"\n        state: \"link\"\n\n    - name: \"{{ my_name }} - Install\"\n      ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n      args:\n        creates: \"{{ retronas_root}} {{ my_dir }}/adtpro.sh\"\n\n    - name: \"{{ my_name }} - create startup service(s) instance\"\n      ansible.builtin.template:\n        src: templates/{{ my_file }}/{{ item.name }}.{{ item.type }}.j2\n        dest: /usr/lib/systemd/system/{{ item.name }}@.{{ item.type }}\n        owner: root\n        group: root\n        mode: 0644\n      with_items: \"{{ systemd_units }}\"\n      notify: \"{{ my_name }} daemon-reload\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n    - name: \"{{ my_name }} daemon-reload\"\n      ansible.builtin.systemd:\n        daemon_reload: true\n"
  },
  {
    "path": "ansible/install_affstools.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"affstools\"\n    my_file: \"install_{{ my_name }}\"\n    module_name: \"affstools\"\n\n    packages:\n      - make\n      - autoconf\n      - gcc\n      - curl\n      - build-essential\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\"}\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - Install from source code\"\n      ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n      args:\n        creates: \"/usr/local/sbin/mkaffs\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_amitools.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"amitools\"\n    my_file: \"install_{{ my_name }}\"\n    module_name: \"amitools\"\n\n    packages:\n      - python3\n      - python3-pip\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\"}\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - Install from source code\"\n      ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_analoguepocket_cifs.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"analoguepocket CIFS\"\n    my_file: \"install_analoguepocket_cifs\"\n    module_name: \"analoguepocket_cifs\"\n    system_key: \"analoguepocket\"\n\n    top_level_paths:\n      - { name: \"Assets\",       enabled: yes,  generic: \"roms\",   systems: yes }\n      - { name: \"Cores\",        enabled: yes,  generic: \"\",       systems: no }\n      - { name: \"Platforms\",    enabled: yes,  generic: \"\",       systems: no }\n      - { name: \"Presets\",      enabled: yes,  generic: \"\",       systems: no }\n      - { name: \"Saves\",        enabled: yes,  generic: \"\",       systems: no }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.extradirs\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba.system\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_apfs-fuse.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"apfs-fuse\"\n    my_file: \"install_apfs-fuse\"\n    module_name: \"apfs-fuse\"\n\n    packages:\n      - git\n      - fuse\n      - libfuse3-dev\n      - bzip2\n      - libbz2-dev\n      - cmake\n      - g++\n      - libattr1-dev\n      - zlib1g-dev\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\"}\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - build source\"\n      ansible.builtin.command:\n        cmd: \"{{ retronas_root }}/scripts/{{ my_file }}.sh\"\n        creates: \"{{ retronas_root }}/bin/apfs-fuse\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_aria2.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"aria2\"\n    my_file: \"install_aria2\"\n    module_name: \"aria2\"\n    module_state: \"present\"\n\n    packages:\n      - aria2\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_assembly64.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"assembly64\"\n    my_file: \"install_{{ my_name }}\"\n    my_dir: /opt/{{ my_name }}\n    module_name: \"assembly64\"\n    module_state: \"present\"\n\n    packages:\n      - xvfb\n      - x11vnc\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"{{ my_name }}_retronas.sh\", dest: \"/opt/{{ my_name }}\", mode: \"0755\" }\n      - { name: \"{{ my_name }}.service\", dest: \"/etc/systemd/system\", mode: \"0644\" }\n\n    paths:\n      - { name: \"{{ my_name }}\", dest: \"/opt\",                state: \"directory\", mode: \"0755\"}\n\n    firewalld_ports:\n      - { port: \"66000\", protocol: tcp}\n\n  tasks:\n\n    - name: \"{{ my_name }} - Include systems map\"\n      ansible.builtin.include_vars: \"retronas_systems.yml\"\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n      notify: \"{{ my_name }} daemon-reload\"\n\n    - name: \"{{ my_name }} - disks symlink\"\n      ansible.builtin.file:\n        src: \"roms/apple/appleii\"\n        dest: \"{{ retronas_path }}/{{ my_name }}\"\n        state: \"link\"\n\n    - name: \"{{ my_name }} - Install\"\n      ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n      args:\n        creates: \"{{ retronas_root}} {{ my_dir }}/{{ my_file }}.sh\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.x11vnc\n\n    - name: \"{{ my_name }} - firewalld\"\n      ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n    - name: \"{{ my_name }} daemon-reload\"\n      ansible.builtin.systemd:\n        daemon_reload: true\n"
  },
  {
    "path": "ansible/install_atarist-sidecart.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  roles:\n    - retronas.role.romdir\n    - retronas.role.nginx\n    - retronas.role.samba\n\n  vars:\n    my_name: \"atarist-sidecart\"\n    my_file: \"install_{{ my_name }}\"\n    base_path: \"{{ retronas_path }}/atarist\"\n    script_url: \"https://raw.githubusercontent.com/diegoparrilla/atarist-sidecart-raspberry-pico/main/roms/update_json\"\n    script_dest: \"{{ retronas_root }}/bin/{{ my_name }}-generate-roms.sh\"\n    module_name: \"atarist-sidecart\"\n\n    packages:\n      - python3-boto3\n\n    paths:\n      - { name: \"bin\",      dest: \"{{ retronas_root }}\" }\n      - { name: \"atarist\",  dest: \"{{ retronas_path }}\" }\n      - { name: \"sidecart\", dest: \"{{ base_path }}\" }\n      - { name: \"db\",       dest: \"{{ base_path }}/sidecart\" }\n\n    links:\n      - { src: \"{{ retronas_path }}/roms/atari/st/cart\", dest: \"{{ base_path }}/roms\" }\n      - { src: \"{{ retronas_path }}/roms/atari/st/flop\", dest: \"{{ base_path }}/floppies\" }\n\n    templates:\n      - { name: \"retronas_atarist.conf\", dest: \"/etc/samba\" }\n      - { name: \"{{ my_name }}-updatedb.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"{{ my_name }}-mirrordb.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"{{ my_name }}-generate-roms.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"99-retronas-sidecart.conf\", dest: \"/etc/nginx/sites-available\" }\n      - { name: \"index.html\", dest: \"{{ base_path }}/sidecart\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n\n    firewalld_rules:\n      - { zone: \"retro\", service: \"http\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Install packages\"\n      ansible.builtin.package:\n        name: \"{{ packages }}\"\n        state: latest\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - symlinks\"\n      ansible.builtin.file:\n        src: \"{{ item.src }}\"\n        dest: \"{{ item.dest }}\"\n        state: \"link\"\n      with_items: \"{{ links }}\"\n\n    - name: \"{{ my_name }} - get update script\"\n      ansible.builtin.shell: curl -kLso \"{{ script_dest }}\" {{ script_url }}\n\n    - name: \"{{ my_name }} - make script executable\"\n      file:\n        path: \"{{ script_dest }}\"\n        mode: '0755'\n\n    - name: \"{{ my_name }} - configure includes file\"\n      ansible.builtin.ini_file:\n        path: /etc/samba/smb.conf\n        section: atarist\n        option: \"include\"\n        value: \"/etc/samba/retronas_atarist.conf\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_batocera_cifs.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"Batocera CIFS\"\n    my_file: \"install_batocera_cifs\"\n    module_name: \"batocera_cifs\"\n    system_key: \"batocera\"\n\n    top_level_paths:\n      - { name: \"ROMS\",   enabled: yes,  generic: \"roms\",   systems: yes }\n      - { name: \"SAVES\",  enabled: yes,  generic: \"saves\",  systems: yes }\n      - { name: \"BIOS\",   enabled: yes,  generic: \"bios\",   systems: yes }\n\n    internal_symlinks:\n      - { src: 'sega/megadrive', dest: 'msu-md' }\n      - { src: 'sharp/mz', dest: 'mz700' }\n      - { src: 'sharp/mz', dest: 'mz800' }\n      - { src: 'sharp/mzb', dest: 'mz2500' }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.extradirs\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba.system\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_cockpit-retronas.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: true\n\n  vars:\n    my_name: \"cockpit-retronas\"\n    my_file: \"install_cockpit-retronas\"\n    package_dir: \"/usr/share/cockpit\"\n    module_name: \"cockpit-retronas\"\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.include_role:\n        name: retronas.role.filesystems\n\n    - ansible.builtin.include_role:\n        name: retronas.role.cockpit\n\n    - name: \"{{ my_name }} - create package directory\"\n      ansible.builtin.file:\n        path: \"{{ package_dir }}/{{ my_name }}\"\n        owner: root\n        group: root\n        mode: 0755\n        state: directory\n\n    - name: \"{{ my_name }} - Generate scripts\"\n      ansible.builtin.template:\n        src: \"templates/{{ my_file }}/{{ my_file }}.sh.j2\"\n        dest: \"{{ retronas_root }}/scripts/{{ my_file }}.sh\"\n        owner: root\n        group: root\n        mode: 0755\n\n    - name: \"{{ my_name }} - build source\"\n      ansible.builtin.command:\n        cmd: \"{{ retronas_root }}/scripts/{{ my_file }}.sh\"\n\n    - ansible.builtin.include_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_cockpit.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: true\n\n  vars:\n    module_name: \"cockpit\"\n\n  roles:\n    - retronas.role.filesystems\n    - retronas.role.apt-backports\n    - retronas.role.cockpit\n    - retronas.role.cockpit-packages\n    - retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_cue2pops.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"cue2pops\"\n    my_file: \"install_cue2pops\"\n    module_name: \"cue2pops\"\n\n    packages:\n      - git\n      - coreutils\n      - make\n      - gcc\n      - g++\n      - build-essential\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\"}\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - Run installer\"\n      ansible.builtin.shell:\n        cmd: \"./{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n        chdir: \"{{ retronas_root }}/scripts\"\n        executable: /bin/bash\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_curlftpfs.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: true\n\n  vars:\n    module_name: \"curlftpfs\"\n\n  roles:\n    - retronas.role.curlftpfs\n    - retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_deluge.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"Deluge\"\n    my_file: \"install_deluge\"\n    module_name: \"bittorrent\"\n\n    packages:\n      - geoip-bin\n      - geoip-database\n      - deluged\n      - deluge-web\n      - deluge-console\n\n    systemd_units:\n      - { name: \"deluged\",    type: 'service', state: \"started\", enabled: \"yes\", restart: \"yes\", instance: \"no\" }\n      - { name: \"deluge-web\", type: 'service', state: \"started\", enabled: \"yes\", restart: \"yes\", instance: \"no\" }\n\n    paths:\n      - { name: \"deluged\",      dest: \"/var/lib\",                       owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n      - { name: \"config\",       dest: \"/var/lib/deluged\",               owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n      - { name: \"bittorrent\",   dest: \"{{ retronas_path }}\",            owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n      - { name: \"torrents\",     dest: \"{{ retronas_path }}/bittorrent\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n      - { name: \"auto-add\",     dest: \"{{ retronas_path }}/bittorrent\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n      - { name: \"downloading\",  dest: \"{{ retronas_path }}/bittorrent\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n      - { name: \"complete\",     dest: \"{{ retronas_path }}/bittorrent\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n\n    templates:\n      - { name: \"autoadd.conf\",       dest: \"/var/lib/deluged/config\" }\n      - { name: \"deluged\",            dest: \"/etc/default\" }\n      - { name: \"core.conf\",          dest: \"/var/lib/deluged/config\" }\n      - { name: \"auth\",               dest: \"/var/lib/deluged/config\" }\n      - { name: \"upgrade_deluge.sh\",  dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"deluged.service\",    dest: '/usr/lib/systemd/system' }\n      - { name: \"deluge-web.service\", dest: '/usr/lib/systemd/system' }\n\n    firewalld_ports:\n      - { zone: retro, port: \"8112\", protocol: \"tcp\" }\n      - { zone: modern, port: \"8112\", protocol: \"tcp\" }\n      - { zone: retro, port: \"58846\", protocol: \"tcp\" }\n      - { zone: modern, port: \"58846\", protocol: \"tcp\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - firewalld\"\n      ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - name: \"{{ my_name }} -remove legacy deluged startup file\"\n      file:\n        path: \"/etc/init.d/deluged\"\n        state: absent\n\n    - name: \"{{ my_name }} - enable service(s)\"\n      ansible.builtin.service:\n        name: \"{{ item.name }}.{{ item.type }}\"\n        state: \"{{ item.state }}\"\n        enabled: \"{{ item.enabled }}\"\n        daemon_reload: true\n      with_items: \"{{ systemd_units }}\"\n      when:\n        - item.instance == 'no'\n      notify: \"{{ my_name }} - Restart service\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item.name }}.{{ item.type }}\"\n        state: restarted\n        daemon_reload: true\n      with_items: \"{{ systemd_units }}\"\n      when:\n        - item.restart == 'yes'\n        - item.instance == 'no'\n"
  },
  {
    "path": "ansible/install_dhcpcd.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"dhcpcd\"\n    my_file: \"install_{{ my_name }}\"\n    module_name: \"dhcpcd\"\n\n    packages:\n      - dhcpcd\n\n    changed: false\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - name: \"{{ my_name }} - restart if imported because handlers will not run\"\n      service:\n        name: \"{{ item }}\"\n        state: restarted\n        enabled: true\n      with_items: \"{{ my_services }}\"\n      when: changed is true\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_disable-laptop-lid.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"Disable Laptop Lid\"\n    my_file: \"install_disable-laptop-lid\"\n    service: \"systemd-logind.service\"\n    module_name: \"disable-laptop-lid\"\n\n    paths:\n      - { name: \"logind.conf.d\", dest: \"/etc/systemd/\",                state: \"directory\", mode: \"0755\"}\n\n    templates:\n      - { name: \"retronas.conf\", dest: \"/etc/systemd/logind.conf.d\", mode: \"0755\"}\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n      notify: \"{{ my_name }} - Restart service\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ service }}\"\n        state: restarted\n"
  },
  {
    "path": "ansible/install_disc-image-creator.yml",
    "content": "# Dependencies\n- ansible.builtin.import_playbook: install_dvdauth.yml\n- ansible.builtin.import_playbook: install_eccedc.yml\n\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"Disc Image Creator\"\n    my_file: \"install_disc-image-creator\"\n    module_name: \"disc-image-creator\"\n\n    packages:\n      - git\n      - coreutils\n      - make\n      - gcc\n      - g++\n      - curl\n      - build-essential\n      - unzip\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts/\", mode: \"0755\"}\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - Run installer\"\n      ansible.builtin.shell:\n        cmd: \"./{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n        chdir: \"{{ retronas_root }}/scripts\"\n        executable: /bin/bash\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_dnsmasq-retro.yml",
    "content": "---\n# Dependencies\n- import_playbook: install_ntp.yml\n- import_playbook: install_dnsmasq.yml\n\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"dnsmasq-retro\"\n    my_service: \"{{ my_name }}\"\n    my_file: \"install_{{ my_name }}\"\n    module_name: \"dnsmasq-retro\"\n\n    templates:\n      - { name: \"dhcp-retro-ethernet.conf\", sub: \"retro\", dest: \"/etc/dnsmasq.d/retro\", force: no }\n      - { name: \"dhcp-retro-wifi.conf\",     sub: \"retro\", dest: \"/etc/dnsmasq.d/retro\", force: no }\n      - { name: \"dhcp.conf\",                sub: \"retro\", dest: \"/etc/dnsmasq.d/retro\", force: no }\n      - { name: \"dns.conf\",                 sub: \"retro\", dest: \"/etc/dnsmasq.d/retro\", force: no } \n      - { name: \"dnsmasq.conf\",             sub: \"retro\", dest: \"/etc/dnsmasq.d/retro\", force: no }\n      - { name: \"interfaces.conf\",          sub: \"retro\", dest: \"/etc/dnsmasq.d/retro\", force: no }\n      - { name: \"ipv6.conf\",                sub: \"retro\", dest: \"/etc/dnsmasq.d/retro\", force: no }\n      - { name: \"dnsmasq-retro.service\",    sub: \"\",      dest: \"/etc/systemd/system\"}\n\n    paths:\n      - { name: \"dnsmasq.d\",                dest: \"/etc\",                 state: \"directory\", mode: \"0755\" }\n      - { name: \"retro\",                    dest: \"/etc/dnsmasq.d\",       state: \"directory\", mode: \"0755\" }\n\n\n    net_settings:\n      - { option: \"interface\",  value: \"{{ retronas_net_retro_interface }}\",  match_regex: \"^interface {{ retronas_net_retro_interface }}$\",    after_regex: \"\",                                               state: \"present\", dest: \"/etc/dhcpcd.conf\" }\n      #- { option: \"interface\",  value: \"{{ retronas_net_wifi_interface }}\",  match_regex: \"^interface {{ retronas_net_wifi_interface }}$\",      after_regex: \"\",                                               state: \"present\", dest: \"/etc/dhcpcd.conf\" }\n      - { option: \"static\",     value: 'ip_address\\={{ retronas_net_retro_ip }}/{{ retronas_net_retro_subnet }}',                               after_regex: \"^interface {{ retronas_net_retro_interface }}$\", state: \"present\", dest: \"/etc/dhcpcd.conf\" }\n      - { option: \"static\",     value: 'domain_name_servers\\={{ retronas_net_retro_dns }}',                                                     after_regex: \"^interface ip_address=.+$\",                      state: \"present\", dest: \"/etc/dhcpcd.conf\" }\n\n    my_services:\n      - dhcpcd\n      - \"{{ my_name }}\"\n\n\n  tasks:\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"{{ my_name }} - mask default dnsmasq service\"\n      service:\n        name: \"dnsmasq\"\n        state: stopped\n        daemon_reload: true\n        enabled: false\n        masked: true\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n      notify: \"{{ my_name }} - Restart services\"\n\n    - name: \"{{ my_name }} - checking for dhcpcd\"\n      stat:\n        path: \"/etc/dhcpcd.conf\"\n      register: dhcpcd_check\n\n    - name: \"{{ my_name }} - setup dhcpcd\"\n      lineinfile:\n        path: \"{{ item.dest }}\"\n        search_string: \"{{ item.match_regex | default('^$') }}\"\n        insertbefore: \"{{ item.before_regex | default(omit) }}\"\n        insertafter: \"{{ item.after_regex | default(omit) }}\"\n        line: \"{{ item.option }} {{ item.value }}\"\n      with_items: \"{{ net_settings }}\"\n      when: dhcpcd_check.stat.exists\n      notify: \"{{ my_name }} - Restart services\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart services\"\n      service:\n        name: \"{{ item }}\"\n        state: restarted\n        daemon_reload: true\n        enabled: true\n      with_items: \"{{ my_service }}\"\n"
  },
  {
    "path": "ansible/install_dnsmasq.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"dnsmasq\"\n    my_file: \"install_{{ my_name }}\"\n    module_name: \"dnsmasq\"\n\n    packages:\n      - dnsmasq\n\n    my_services:\n      - dnsmasq\n\n    changed: false\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - name: \"{{ my_name }} - restart if imported because handlers will not run\"\n      service:\n        name: \"{{ item }}\"\n        state: restarted\n        enabled: true\n      with_items: \"{{ my_services }}\"\n      when: changed is true\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_doc.yml",
    "content": "# Dependencies\n- ansible.builtin.import_playbook: install_lynx.yml\n\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"retronas_docs\"\n    my_file: \"install_retronas_docs\"\n    module_name: \"retronas_docs\"\n\n    packages:\n      - git\n      - pandoc\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"{{ my_name }} - Clone local copy of RetroNAS wiki\"\n      ansible.builtin.git:\n        repo: https://github.com/retronas/retronas.wiki.git\n        dest: \"{{ retronas_root }}/doc\"\n        update: false\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_dreampi.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: true\n\n  vars:\n    my_name: \"dreampi\"\n    my_file: \"install_dreampi\"\n    module_name: \"dreampi\"\n\n    packages:\n      - git\n      - dnsmasq\n      - dnsutils\n      - libnetfilter-queue-dev\n      - libnetfilter-queue1\n      - ppp\n      - arping\n      - nftables\n      - tcpd\n      - wvdial\n      - python3-pip\n\n    groups:\n      - dialout\n      - dip\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"{{ my_name }}.conf\", dest: \"/etc/dnsmasq.d\", force: no }\n\n    paths:\n      - { name: \"dnsmasq.d\", dest: \"/etc\" }\n\n    systemd_patches:\n      - { unit: \"dreampi.service\", line: \"Restart=on-failure\", regex: \"^Restart=on-failure$\", after: \"^ExecStart=.*$\" }\n      - { unit: \"dreampi.service\", line: \"RestartSec=30s\", regex: \"^RestartSec=.+\", after: \"^Restart=.*$\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Checking hw platform compatiblity\"\n      ansible.builtin.debug:\n        msg: \"INCOMPATIBLE PLATFORM, only arm platforms are supported\"\n      when: ansible_architecture is not search('arm') and\n            ansible_architecture != 'aarch64'\n\n    - ansible.builtin.meta: end_play\n      when: ansible_architecture is not search('arm') and\n            ansible_architecture != 'aarch64'\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - Run installer\"\n      ansible.builtin.shell:\n        cmd: \"./{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n        chdir: \"{{ retronas_root }}/scripts\"\n        executable: /bin/bash\n      notify: \"{{ my_name }} Restart Services\"\n\n    - name: \"{{ my_name }} - Patch systemd service\"\n      ansible.builtin.lineinfile:\n        path: /etc/systemd/system/{{ item.unit }}\n        regex: \"{{ item.regex }}\"\n        insertafter: \"{{ item.after }}\"\n        line: \"{{ item.line }}\"\n        state: present\n      with_items: \"{{ systemd_patches }}\"\n      notify: \"{{ my_name }} Restart Services\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n    - name: \"{{ my_name }} Restart Services\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: started\n        enabled: true\n        daemon_reload: true\n      with_items:\n        - \"{{ my_name }}.service\"\n"
  },
  {
    "path": "ansible/install_dvdauth.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"DVDAuth\"\n    my_file: \"install_dvdauth\"\n    module_name: \"dvdauth\"\n\n    packages:\n      - git\n      - coreutils\n      - make\n      - gcc\n      - g++\n      - curl\n      - build-essential\n      - unzip\n      - util-linux\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\"}\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - Run installer\"\n      ansible.builtin.shell:\n        cmd: \"./{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n        chdir: \"{{ retronas_root }}/scripts\"\n        executable: /bin/bash\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_eccedc.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"EccEDC\"\n    my_file: \"install_eccedc\"\n    module_name: \"eccedc\"\n\n    packages:\n      - coreutils\n      - make\n      - gcc\n      - g++\n      - curl\n      - build-essential\n      - unzip\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\"}\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - Run installer\"\n      ansible.builtin.shell:\n        cmd: \"./{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n        chdir: \"{{ retronas_root }}/scripts\"\n        executable: /bin/bash\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_emudeck_cifs.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"EmuDeck CIFS\"\n    my_file: \"install_emudeck_cifs\"\n    system_key: \"emudeck\"\n    module_name: \"emudeck_cifs\"\n\n    top_level_paths:\n      - { name: \"roms\",   enabled: yes,  generic: \"roms\",   systems: yes }\n    #  - { name: \"saves\",  enabled: yes,  generic: \"saves\",  systems: yes }\n      - { name: \"bios\",   enabled: yes,  generic: \"bios\",   systems: yes }\n\n    internal_symlinks:\n      - { src: 'commodore/amiga', dest: 'ags' }\n      - { src: 'commodore/amiga', dest: 'amiga600' }\n      - { src: 'atari/jaguar', dest: 'atarijaguarcd' }\n      - { src: 'capcom/cps1', dest: 'cps' }\n      - { src: 'nintendo/gamecube', dest: 'gc' }\n      - { src: 'mame/mame', dest: 'mame-advmame' }\n      - { src: 'mame/mame', dest: 'mame-mame4all' }\n      - { src: 'mame/mame', dest: 'mame-mame4all' }\n      - { src: 'sega/megacd', dest: 'segacd' }\n      - { src: 'sega/megacd', dest: 'megacdjp' }\n      - { src: 'sega/megadrive', dest: 'genesis' }\n      - { src: 'sega/megadrive', dest: 'megadrivejp' }\n      - { src: 'nintendo/3ds', dest: 'n3ds' }\n      - { src: 'snk/neogeocd', dest: 'neogeocdjp' }\n      - { src: 'sony/playstation1', dest: 'psx' }\n      - { src: 'sega/saturn', dest: 'saturnjp' }\n      - { src: 'sega/32x', dest: 'sega32xjp' }\n      - { src: 'sega/32x', dest: 'sega32xna' }\n      - { src: 'nintendo/superfamicom', dest: 'sneshd' }\n      - { src: 'nintendo/superfamicom', dest: 'snesna' }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.extradirs\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba.system\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_emuelec_cifs.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"EmuElec CIFS\"\n    my_file: \"install_emuelec_cifs\"\n    module_name: \"emuelec_cifs\"\n    system_key: \"emuelec\"\n\n    internal_symlinks:\n      - { src: 'nintendo/famicom', dest: 'nesh' }\n      - { src: 'nintendo/gameboyadvance', dest: 'gbah' }\n      - { src: 'nintendo/gameboy', dest: 'gbh' }\n      - { src: 'nintendo/gameboycolor', dest: 'gbch' }      \n      - { src: 'nintendo/superfamicom', dest: 'snesh' }\n      - { src: 'sega/gamegear', dest: 'gamegearh' }\n      - { src: 'sega/megadrive', dest: 'genh' }\n\n  tasks:\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.extradirs\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba.system\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_etherdfs.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"EtherDFS\"\n    my_file: \"install_etherdfs\"\n    module_name: \"etherdfs\"\n\n    packages:\n      - make\n      - automake\n      - autoconf\n      - gcc\n      - g++\n      - git\n      - build-essential\n      - coreutils\n\n    paths:\n      - { name: \"dos\", dest: \"{{ retronas_path }}\", state: \"directory\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\", mode: \"0755\" }\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"etherdfs.service\", dest: \"/usr/lib/systemd/system/\" }\n      - { name: \"retronas_dos.conf\", dest: \"/etc/samba/\" }\n\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - Install from source code\"\n      ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n      args:\n        creates: \"{{ retronas_root }}/bin/ethersrv\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - configure includes file\"\n      ansible.builtin.ini_file:\n        path: /etc/samba/smb.conf\n        section: dos\n        option: \"include\"\n        value: \"/etc/samba/retronas_dos.conf\"\n      notify: \"{{ my_name }} - Install Samba\"\n\n    - name: \"{{ my_name }} - enable startup service\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: started\n        enabled: true\n        daemon_reload: true\n      with_items:\n        - etherdfs\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba\n      vars:\n        changed: \"{{ install_changed }}\"\n      when: install_changed is defined and\n            install_changed is true\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: restarted\n        daemon_reload: true\n      with_items:\n        - etherdfs\n\n    - name: \"{{ my_name }} - Install Samba\"\n      set_fact:\n        install_changed: true\n"
  },
  {
    "path": "ansible/install_ethflopd.yml",
    "content": "- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"ethflopd\"\n    my_file: \"install_{{ my_name }}\"\n    module_name: \"ethflopd\"\n\n    packages:\n      - make\n      - automake\n      - autoconf\n      - gcc\n      - g++\n      - git\n      - build-essential\n      - coreutils\n\n    paths:\n      - { name: \"dos\", dest: \"{{ retronas_path }}\", state: \"directory\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\", mode: \"0755\" }\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"ethflopd.service\", dest: \"/usr/lib/systemd/system/\" }\n\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - Install from source code\"\n      ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n      args:\n        creates: \"{{ retronas_root }}/bin/{{ my_name }}\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - enable startup service\"\n      ansible.builtin.service:\n        name: \"{{ my_name }}.service\"\n        state: started\n        enabled: true\n        daemon_reload: true\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ my_name }}.service\"\n        state: restarted\n        daemon_reload: true\n"
  },
  {
    "path": "ansible/install_extract-xiso.yml",
    "content": "- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"extract-xiso\"\n    my_file: \"install_extract-xiso\"\n    module_name: \"extract-xiso\"\n\n    packages:\n      - make\n      - gcc\n      - coreutils\n      - cmake\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - build source\"\n      ansible.builtin.command:\n        cmd: \"{{ retronas_root }}/scripts/{{ my_file }}.sh\"\n        creates: \"/usr/local/bin/extract-xiso\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_extradirs.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    module_name: \"extradirs\"\n\n  tasks:\n    - ansible.builtin.import_role:\n        name: retronas.role.extradirs\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_far2l.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"far2l\"\n    my_file: \"install_{{ my_name }}\"\n    module_name: \"far2l\"\n\n    packages:\n      - gawk\n      - m4\n      - libpcre2-dev\n      - libxerces-c-dev\n      - libspdlog-dev\n      - libuchardet-dev\n      - libssh-dev\n      - libssl-dev\n      - libsmbclient-dev\n      - libnfs-dev\n      - libneon27-dev\n      - libarchive-dev\n      - cmake\n      - g++\n      - gcc\n      - git\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - build source\"\n      ansible.builtin.command:\n        cmd: \"{{ retronas_root }}/scripts/{{ my_file }}.sh\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_fenrir-ode-webserver.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"fenrir-ode-webserver\"\n    my_file: \"install_fenrir-ode-webserver\"\n    module_name: \"fenrir-ode-webserver\"\n\n    packages:\n      - make\n      - gcc\n      - g++\n      - coreutils\n      - cmake\n\n    fenrir_ode_path: \"{{ retronas_path }}/fenrir-ode-webserver\"\n    fenrir_ode_port: \"31994\"\n    fenrir_ode_bin: \"/usr/local/bin/FenrirServer\"\n\n    my_service: \"{{ my_name }}.service\"\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"{{ my_name }}.service\", dest: \"/usr/lib/systemd/system\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n      notify: \"{{ my_name }} - Restart service\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - run build tool\"\n      ansible.builtin.shell:\n        cmd: \"{{ retronas_root }}/scripts/{{ my_file }}.sh\"\n        creates: \"{{ fenrir_ode_bin }}\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - create share link\"\n      ansible.builtin.file:\n        src: \"roms/sega/saturn\"\n        dest: \"{{ fenrir_ode_path }}\"\n        state: link\n      notify: \"{{ my_name }} - Restart service\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: restarted\n        enabled: true\n        daemon_reload: true\n      with_items: \"{{ my_service }}\"\n"
  },
  {
    "path": "ansible/install_filesystems.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    module_name: \"filesystems\"\n\n  roles:\n    - retronas.role.filesystems\n    - retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_firewalld-zones.yml",
    "content": "---\n# Dependencies\n- ansible.builtin.import_playbook: install_dnsmasq.yml\n- ansible.builtin.import_playbook: install_firewalld.yml\n\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"firewalld-zones\"\n    my_service: firewalld\n    my_file: \"install_{{ my_name }}\"\n    module_name: \"firewalld-zones\"\n\n    templates:\n      - { name: \"retro_to_modern.xml\",      sub: \"policies\",    dest: \"/etc/{{ my_service }}/policies\",                 force: no }\n      - { name: \"samba-modern.xml\",         sub: \"services\",    dest: \"/etc/{{ my_service }}/services\",                 force: no }\n      - { name: \"ps3netsrv.xml\",            sub: \"services\",    dest: \"/etc/{{ my_service }}/services\",                 force: no }\n      - { name: \"modern.xml\",               sub: \"zones\",       dest: \"/etc/{{ my_service }}/zones\",                    force: no }\n      - { name: \"retro.xml\",                sub: \"zones\",       dest: \"/etc/{{ my_service }}/zones\",                    force: no } \n\n    paths:\n      - { name: \"{{ my_service }}\",               dest: \"/etc\",                  state: \"directory\", mode: \"0750\" }\n      - { name: \"{{ my_service }}/policies\",      dest: \"/etc\",                  state: \"directory\", mode: \"0750\" }\n      - { name: \"{{ my_service }}/services\",      dest: \"/etc\",                  state: \"directory\", mode: \"0750\" }\n      - { name: \"{{ my_service }}/zones\",         dest: \"/etc\",                  state: \"directory\", mode: \"0750\" }\n\n    firewalld_rules:\n      - { zone: \"retro\", service: \"ssh\" }\n      - { zone: \"modern\", service: \"ssh\" }\n      - { zone: \"retro\", service: \"cockpit\" }\n      - { zone: \"modern\", service: \"cockpit\" }\n\n  tasks:\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - Reload firewalld to pickup the new zones\"\n      ansible.builtin.service:\n        name: \"firewalld\"\n        state: restarted\n        daemon_reload: true\n        enabled: true\n        force: true\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n    - name: \"{{ my_name }} - Reload firewalld\"\n      ansible.builtin.set_fact:\n        install_changed: true\n\n- ansible.builtin.import_playbook: install_firewalld.yml\n  vars:\n    changed: \"{{ install_changed }}\"\n  when: install_changed is defined and\n        install_changed is true\n"
  },
  {
    "path": "ansible/install_firewalld.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"firewalld\"\n    my_file: \"install_{{ my_name }}\"\n    my_services: \"firewalld.service\"\n    module_name: \"firewalld\"\n\n    templates:\n      - { name: \"clear-python-bytecode\",    sub: \"workarounds\", dest: \"/usr/local/sbin\",              mode: '0750' }\n      - { name: \"override.conf\",            sub: \"workarounds\", dest: \"/etc/systemd/system\" }\n\n    paths:\n      - { name: \"{{ my_name }}\",               dest: \"/etc\",                  state: \"directory\", mode: \"0750\" }\n      - { name: \"{{ my_name }}.service.d\",     dest: \"/etc/systemd/system/\",  state: \"directory\", mode: \"0750\" }\n\n    packages:\n      - firewalld\n      - python3-firewall\n\n    collections:\n      - { collection: ansible.posix, creates: ansible/posix }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - install related ansible collections\"\n      ansible.builtin.command:\n        cmd: /usr/bin/ansible-galaxy collection install {{ item.collection }}\n        creates: /opt/retronas/ansible/collections/ansible_collections/{{ item.creates }}\n      with_items: \"{{ collections }}\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: restarted\n        daemon_reload: true\n        enabled: true\n        force: true\n      with_items: \"{{ my_services }}\"\n"
  },
  {
    "path": "ansible/install_flippydrive.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: true\n\n  vars:\n    my_name: \"Flippydrive\"\n    my_file: \"install_flippydrive\"\n    my_service: \"flippydrive\"\n    module_name: \"flippydrive\"\n\n    paths:\n      - { name: \"gamecube\", dest: \"{{ retronas_path }}\" }\n      - { name: \"{{ my_service }}\", dest: \"/opt\" }\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"{{ my_service }}.sh\", dest: \"/opt/{{ my_service }}\", mode: \"0755\" }\n      - { name: \"{{ my_service }}.service\", dest: \"/etc/systemd/system/\" }\n\n    firewalld_ports:\n      - { port: 7031, protocol: tcp }\n\n    packages:\n      - unzip\n      - curl\n\n  tasks:\n\n    - ansible.builtin.assert:\n        that: ansible_architecture == \"x86_64\"\n        fail_msg: \"Unsupported architecture\"\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"{{ my_name }} - Load RetroNAS systems\"\n      ansible.builtin.include_vars: retronas_systems.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - Install from repo\"\n      ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - enable startup service\"\n      ansible.builtin.service:\n        name: \"{{ my_service }}\"\n        state: started\n        enabled: true\n        daemon_reload: true\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ my_service }}\"\n        state: restarted\n        daemon_reload: false\n"
  },
  {
    "path": "ansible/install_freestation.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: true\n\n  vars:\n    my_name: \"freestation\"\n    my_file: \"install_{{ my_name }}\"\n    module_name: \"freestation\"\n    system_key: \"freestation\"\n\n    top_level_paths:\n      - { name: \"games\",      enabled: yes,  generic: \"roms\",        systems: yes }\n      - { name: \"bios\",       enabled: yes,  generic: \"bios\",        systems: yes }\n\n    templates:\n      - { name: \"retronas_freestation_cifs.conf\", name_dest: \"retronas_freestation.conf\", dest: \"/etc/samba\"}\n      - { name: \"retronas_freestation_nfs.conf\",  name_dest: \"retronas_freestation.conf\", dest: \"/etc/exports.d\"}\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.extradirs\n\n    - ansible.builtin.import_role:\n        name: retronas.role.nfs\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba.system\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_fsp.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"FSP for Swiss\"\n    my_file: \"install_fsp\"\n    my_service: \"fspd\"\n    module_name: \"fsp\"\n\n    packages:\n      - python3\n      - python-is-python3\n      - build-essential\n      - flex\n      - gcc\n      - scons\n\n    swiss_rw:\n      - .FSP_OK_ADD\n      - .FSP_OK_DEL\n      - .FSP_OK_MKDIR\n      - .FSP_OK_RENAME\n\n    paths:\n      - { name: \"fsp\",      dest: \"{{ retronas_root }}/bin/\" }\n      - { name: \"etc\",      dest: \"{{ retronas_root }}/bin/fsp/\" }\n      - { name: \"gamecube\", dest: \"{{ retronas_path }}\" }\n      - { name: \"swiss\",    dest: \"{{ retronas_path }}/gamecube\" }\n      - { name: \"swiss\",    dest: \"{{ retronas_path }}/gamecube/swiss\" }   # ????\n      - { name: \"tmp\",      dest: \"{{ retronas_path }}/gamecube/swiss\" }\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\"}\n      - { name: \"{{ my_service }}.conf\", dest: \"{{ retronas_root }}/bin/fsp/etc\" }\n      - { name: \"fspd.service\", dest: \"/usr/lib/systemd/system/\" }\n\n    firewalld_ports:\n      - { port: 2121, protocol: udp }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"{{ my_name }} - Load RetroNAS systems\"\n      ansible.builtin.include_vars: retronas_systems.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - name: \"{{ my_name }} - build layout\"\n      ansible.builtin.file:\n        src: \"../../roms/{{ item.src }}\"\n        dest: \"{{ retronas_path }}/gamecube/swiss/{{ item.fspd }}\"\n        owner: \"{{ retronas_user }}\"\n        group: \"{{ retronas_group }}\"\n        state: link\n      loop: \"{{ system_map }}\"\n      when:\n        item.fspd | length > 0\n      notify: \"{{ my_name }} - Restart service\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - Install from source code\"\n      ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - Set write permissions top level\"\n      ansible.builtin.copy:\n        content: \"\"\n        dest: \"{{ retronas_path }}/gamecube/{{ item.0 }}/{{ item.1 }}\"\n        force: false\n        owner: \"{{ retronas_user }}\"\n        group: \"{{ retronas_group }}\"\n        mode: 0644\n      with_nested:\n        - [\"swiss\", \"swiss/swiss\"]\n        - \"{{ swiss_rw }}\"\n\n    - name: \"{{ my_name }} - enable startup service\"\n      ansible.builtin.service:\n        name: \"{{ my_service }}\"\n        state: started\n        enabled: true\n        daemon_reload: true\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ my_service }}\"\n        state: restarted\n        daemon_reload: false\n"
  },
  {
    "path": "ansible/install_gogrepo.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"gogrepo\"\n    my_file: \"install_gogrepo\"\n    module_name: \"gogrepo\"\n\n    packages:\n      - python3\n      - python-is-python3\n      - python3-html5lib\n      - git\n      - sudo\n\n    templates:\n      - { name: \"gogrepo_login.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\"}\n      - { name: \"gogrepo_import-cookies.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\"}\n      - { name: \"gogrepo_download.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\"}\n      - { name: \"gogrepo_update.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\"}\n      - { name: \"gogrepo-wrapper.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\"}\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - name: \"{{ my_name }} - create GOG dir\"\n      ansible.builtin.file:\n        path: \"{{ retronas_path }}/{{ item }}\"\n        owner: \"{{ retronas_user }}\"\n        group: \"{{ retronas_group }}\"\n        mode: \"0775\"\n        state: directory\n      with_items:\n        - gog\n\n    - name: \"{{ my_name }} - download gogrepo+patches (sairuk)\"\n      ansible.builtin.shell:\n        chdir: \"{{ retronas_root }}/bin\"\n        cmd: \"git clone https://github.com/sairuk/gogrepo.git\"\n        creates: \"{{ retronas_root }}/bin/gogrepo\"\n\n    - name: \"{{ my_name }} - update gogrepo\"\n      ansible.builtin.shell:\n        chdir: \"{{ retronas_root }}/bin/gogrepo\"\n        cmd: \"git pull\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_hb-store-cdn.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: true\n\n  vars:\n    my_name: hb-store-cdn\n    my_file: \"install_{{ my_name }}\"\n    module_name: \"hb-store-cdn\"\n\n    systemd_units:\n      - { name: \"{{ my_name }}\", type: 'service', state: \"started\", enabled: \"yes\", restart: \"yes\", instance: \"no\" }\n\n    packages:\n      - git\n      - npm\n      - gcc\n      - g++\n      - build-essential\n\n    packages_debian:\n      debian12:\n        - libnode108\n      debian13:\n        - libnode115\n\n    paths:\n      - { name: \"{{ my_name }}\",    dest: \"{{ retronas_root }}/bin\",                state: \"directory\", mode: \"0755\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n      - { name: \"bin\",              dest: \"{{ retronas_root }}/bin/{{ my_name }}\",  state: \"directory\", mode: \"0755\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n      - { name: \"ps4\",              dest: \"{{ retronas_path }}\",                    state: \"directory\", mode: \"0755\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n\n    templates:\n      - { name: \"{{ my_name }}-check.sh\", dest: \"{{ retronas_root }}/bin/{{ my_name }}\", mode: \"0755\"}\n      - { name: \"{{ my_name }}.cron\", dest: \"/etc/cron.d\" }\n      - { name: \"config.ini\", dest: \"{{ retronas_root }}/bin/{{ my_name }}\" }\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"{{ my_name }}.service\", dest: \"/usr/lib/systemd/system\" }\n\n    firewalld_ports:\n      - { port: 6449, protocol: \"tcp\" }\n\n  tasks:\n    - name: \"{{ my_name }} - Include systems map\"\n      ansible.builtin.include_vars: \"retronas_systems.yml\"\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n      vars:\n        packages: \"{{ packages_debian['debian' + ansible_distribution_major_version ] }}\"\n      when: ansible_distribution == 'Debian'\n      notify: \"{{ my_name }} - Restart service\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - name: \"{{ my_name }} - build layout\"\n      ansible.builtin.file:\n        src: \"{{ retronas_path }}/roms/sony/playstation4/pkg\"\n        dest: \"{{ retronas_path }}/ps4/{{ my_name }}\"\n        state: link\n      notify: \"{{ my_name }} - Restart service\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - Install from source code\"\n      ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n      args:\n        creates: \"{{ retronas_root }}/bin/{{ my_name }}/hb-store-cdn-cli-server\"\n\n    - name: \"{{ my_name }} - enable service(s)\"\n      ansible.builtin.service:\n        name: \"{{ item.name }}.{{ item.type }}\"\n        state: \"{{ item.state }}\"\n        enabled: \"{{ item.enabled }}\"\n        daemon_reload: true\n      with_items:\n        \"{{ systemd_units }}\"\n      when:\n        - item.instance == 'no'\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - enable service(s)\"\n      ansible.builtin.service:\n        name: \"{{ item.name }}.{{ item.type }}\"\n        state: \"{{ item.state }}\"\n        enabled: \"{{ item.enabled }}\"\n        daemon_reload: true\n      with_items:\n        \"{{ systemd_units }}\"\n      when:\n        - item.instance == 'no'\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item.name }}.{{ item.type }}\"\n        state: restarted\n        daemon_reload: true\n      with_items:\n        \"{{ systemd_units }}\"\n      when:\n        - item.restart == 'yes'\n        - item.instance == 'no'\n\n    - name: \"{{ my_name }} - Restart instances\"\n      ansible.builtin.service:\n        name: \"{{ item.1.name }}{{ item.0.dest }}.{{ item.1.type }}\"\n        state: restarted\n        daemon_reload: true\n      with_items:\n        - \"{{ system_map }}\"\n        - \"{{ systemd_units }}\"\n      when:\n        - item.1.restart == 'yes'\n        - item.1.instance == 'yes'\n"
  },
  {
    "path": "ansible/install_hdldump.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"hdldump\"\n    my_file: \"install_{{ my_name }}\"\n    module_name: \"hdldump\"\n\n    packages:\n      - make\n      - gcc\n      - coreutils\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\"}\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - build source\"\n      ansible.builtin.command:\n        cmd: \"{{ retronas_root }}/scripts/{{ my_file }}.sh\"\n        creates: \"/usr/local/bin/hdldump\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_hdparm.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"hdparm\"\n    my_file: \"install_{{ my_name }}\"\n    module_name: \"hdparm\"\n\n    packages:\n      - hdparm\n      - coreutils\n\n    scripts:\n      - \"{{ my_name }}.sh\"\n      - \"{{ my_name }}-manager.sh\"\n\n    symlinks:\n      - hdparm-manager-disable-apm\n      - hdparm-manager-disable-standby\n      - hdparm-manager-start-service\n      - hdparm-manager-query-service\n      - hdparm-manager-stop-service\n      - hdparm-manager-drive-selector\n\n    systemd_units:\n      - { name: \"{{ my_name }}\", type: \"timer\", state: \"stopped\", instance: \"no\", enabled: \"no\", restart: \"no\" }\n      - { name: \"{{ my_name }}\", type: \"service\", state: \"stopped\", instance: \"no\", enabled: \"no\", restart: \"no\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - name: \"{{ my_name }} - check dir\"\n      ansible.builtin.file:\n        path: /usr/lib/systemd/system\n        owner: root\n        group: root\n        mode: 0755\n        state: directory\n\n    - name: \"{{ my_name }} - install script\"\n      ansible.builtin.template:\n        src: \"templates/{{ my_file }}/{{ item }}.j2\"\n        dest: \"{{ retronas_root }}/scripts/{{ item }}\"\n        owner: root\n        group: root\n        mode: '0755'\n      with_items:\n        - \"{{ scripts }}\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - create startup service\"\n      ansible.builtin.template:\n        src: templates/{{ my_file }}/{{ item.name }}.{{ item.type }}.j2\n        dest: /usr/lib/systemd/system/{{ item.name }}.{{ item.type }}\n        owner: root\n        group: root\n        mode: 0644\n      with_items:\n        - \"{{ systemd_units }}\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - create symlinks for webui\"\n      ansible.builtin.file:\n        src: \"{{ retronas_root }}/scripts/{{ my_name }}-manager.sh\"\n        dest: \"{{ retronas_root }}/scripts/{{ item }}.sh\"\n        owner: root\n        group: root\n        state: link\n      with_items:\n        - \"{{ symlinks }}\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - enable startup service\"\n      ansible.builtin.service:\n        name: \"{{ item.name }}\"\n        state: started\n        enabled: \"{{ item.enabled }}\"\n        daemon_reload: true\n      with_items:\n        - \"{{ systemd_units }}\"\n      when:\n        - item.enabled == \"yes\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: restarted\n        daemon_reload: true\n      with_items:\n        - \"{{ systemd_units }}\"\n      when:\n        - item.restart == \"yes\"\n"
  },
  {
    "path": "ansible/install_hfsutils.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: true\n\n  vars:\n    my_name: \"hfsutils\"\n    my_file: \"install_hfsutils\"\n    module_name: \"hfsutils\"\n\n    packages:\n      - git\n      - autoconf\n      - build-essential\n\n    templates:\n      - { name: \"{{ my_file }}.sh\",         dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Include systems map\"\n      ansible.builtin.include_vars: \"retronas_systems.yml\"\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - Install from source code\"\n      ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_hostapd.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"hostapd\"\n    my_file: \"install_{{ my_name }}\"\n    module_name: \"hostapd\"\n\n    packages:\n      - hostapd\n      - pwgen\n\n    my_services: \"hostapd-retronas.service\"\n\n    templates:\n      - { name: \"hostapd-retronas.conf\",    sub: \"\", dest: \"/etc/{{ my_name }}\", force: no, mode: \"0640\" }\n      - { name: \"hostapd-dnsmasq.conf\",     sub: \"\", dest: \"/etc/dnsmasq.d/retro/\", force: no, mode: \"0640\" }\n      - { name: \"hostapd-retronas.service\", sub: \"\", dest: \"/etc/systemd/system\" }\n\n    paths:\n      - { name: \"dnsmasq.d\",       dest: \"/etc\",              state: \"directory\", mode: \"0755\" }\n      - { name: \"retro\",           dest: \"/etc/dnsmasq.d\",    state: \"directory\", mode: \"0755\" }\n\n\n  tasks:\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - name: \"{{ my_name }} - generate password\"\n      shell:\n        cmd: pwgen -s 12 1\n      no_log: true\n      register: retronas_wifi_password\n\n    - name: \"{{ my_name }} - generate unique ssid\"\n      shell:\n        cmd: echo \"retronas-$(pwgen 8 -A -0 -B 1)\"\n      no_log: true\n      register: retronas_net_wifi_ssid\n      when: retronas_net_wifi_ssid == \"\"\n\n    - name: \"{{ my_name }} - update wifi ssid config\"\n      lineinfile:\n        path: /opt/retronas/ansible/retronas_vars.yml\n        regexp: ^retronas_net_wifi_ssid.*\n        line: 'retronas_net_wifi_ssid: \"{{ retronas_net_wifi_ssid.stdout_lines[0] }}\"'\n      when: retronas_net_wifi_ssid is defined\n\n    - name: \"{{ my_name }}  - Re-read RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - enable\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: started\n        enabled: true\n        daemon_reload: true\n        force: true\n      with_items: \"{{ my_services }}\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: restarted\n        daemon_reload: true\n        force: true\n      with_items: \"{{ my_services }}\"\n"
  },
  {
    "path": "ansible/install_kermit.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"kermit\"\n    my_file: \"install_{{ my_name }}\"\n    module_name: \"kermit\"\n\n    packages:\n      - ckermit\n\n    systemd_units:\n      - { name: \"iksd.socket\", enabled: yes, state: 'restarted' }\n\n    templates:\n      - { name: \"iksd@.service\", dest: \"/etc/systemd/system\" }\n      - { name: \"iksd.socket\", dest: \"/etc/systemd/system\" }\n\n    firewalld_ports:\n      - { port: 1649, protocol: tcp }\n\n  tasks:\n\n    - name: \"{{ my_name }}  - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - manage startup services\"\n      ansible.builtin.service:\n        name: \"{{ item.name }}\"\n        state: \"{{ item.state|default('stopped') }}\"\n        enabled: \"{{ item.enabled|default('no') }}\"\n        daemon_reload: true\n      with_items: \"{{ systemd_units }}\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_lighttpd.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"lighttpd\"\n    my_file: \"install_{{ my_name }}\"\n    module_name: \"lighttpd\"\n\n    packages:\n      - { name: \"nginx\", state: \"absent\" }\n      - { name: \"{{ my_name }}\", state: \"latest\" }\n\n    config_settings_absent:\n      - { section: null, option: \"server.document-root\"}\n      - { section: null, option: \"server.username\"}\n      - { section: null, option: \"server.groupname\"}\n\n    templates:\n      - { name: \"99-retronas.conf\", dest: \"/etc/{{ my_name }}/conf-available\", mode: \"0640\"}\n\n    conf_enable:\n      - \"99-retronas.conf\"\n\n    systemd_units:\n      - { name: \"{{ my_name }}\", type: 'service', state: \"started\", enabled: \"yes\", restart: \"yes\", instance: \"no\" }\n\n    firewalld_rules:\n      - { service: \"http\" }\n      - { service: \"https\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"{{ my_name }} - manage packages\"\n      ansible.builtin.package:\n        name: \"{{ item.name }}\"\n        state: \"{{ item.state }}\"\n      with_items: \"{{ packages }}\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - configure main config\"\n      ansible.builtin.ini_file:\n        path: /etc/{{ my_name }}/{{ my_name }}.conf\n        section: \"{{ item.section }}\"\n        option: \"{{ item.option }}\"\n        state: absent\n      with_items: \"{{ config_settings_absent }}\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - enable conf\"\n      ansible.builtin.file:\n        src: /etc/{{ my_name }}/conf-available/{{ item }}\n        dest: /etc/{{ my_name }}/conf-enabled/{{ item }}\n        state: \"link\"\n      with_items: \"{{ conf_enable }}\"\n\n    - name: \"{{ my_name }} - set logfile permissions\"\n      ansible.builtin.file:\n        path: /var/log/{{ my_name }}\n        owner: \"{{ retronas_user }}\"\n        group: \"{{ retronas_group }}\"\n        state: directory\n        recurse: true\n\n    - name: \"{{ my_name }} - set run dir permissions\"\n      ansible.builtin.file:\n        path: /var/run/{{ my_name }}\n        owner: \"{{ retronas_user }}\"\n        group: \"{{ retronas_group }}\"\n        state: directory\n        recurse: true\n\n    - name: \"{{ my_name }} - configure server.errorlog\"\n      ansible.builtin.ini_file:\n        path: /etc/{{ my_name }}/{{ my_name }}.conf\n        section: null\n        option: server.errorlog\n        state: absent\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - enable startup services\"\n      ansible.builtin.service:\n        name: \"{{ item.name }}\"\n        state: \"{{ item.state }}\"\n        enabled: \"{{ item.enabled }}\"\n      with_items: \"{{ systemd_units }}\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item.name }}\"\n        state: restarted\n      with_items: \"{{ systemd_units }}\"\n      when: item.restart == \"yes\"\n"
  },
  {
    "path": "ansible/install_linux-dexdrive.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"linux-dexdrive\"\n    my_file: \"install_{{ my_name }}\"\n    module_name: \"linux-dexdrive\"\n\n    packages:\n      - git\n      - build-essential\n\n    templates:\n      - { name: \"{{ my_file }}.sh\",   dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"makefile.patch\",     dest: \"/tmp\" }\n      - { name: \"dexdrive_dumper.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"{{ my_name }}.service\", dest: \"/etc/systemd/system\" }\n\n    systemd_units:\n      - { name: \"{{ my_name }}\", type: 'service', state: \"stopped\", enabled: \"no\", restart: \"no\", instance: \"no\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"{{ my_name }} - Load RetroNAS systems\"\n      ansible.builtin.include_vars: retronas_systems.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n      notify: \"{{ my_name }} daemon-reload\"\n\n    - name: \"{{ my_name }} - build source\"\n      ansible.builtin.shell:\n        cmd: \"{{ retronas_root }}/scripts/{{ my_file }}.sh\"\n        creates: \"/usr/local/bin/dexattach\"\n\n    - name: \"{{ my_name }} - dexdrive module\"\n      ansible.builtin.lineinfile:\n        path: /etc/modules-load.d/modules.conf\n        regex: \"^dexdrive$\"\n        line: \"dexdrive\"\n\n    - name: \"{{ my_name }} - {{ retronas_user }} groups\"\n      ansible.builtin.user:\n        name: \"{{ retronas_user }}\"\n        group: \"disk\"\n        append: true\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n    - name: \"{{ my_name }} daemon-reload\"\n      ansible.builtin.systemd:\n        daemon_reload: true\n"
  },
  {
    "path": "ansible/install_linux-gadgets.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"linux-gadgets\"\n    my_file: \"install_{{ my_name }}\"\n    module_name: \"linux-gadgets\"\n\n    templates:\n      - { name: \"gadget-mass-storage-manage.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\"}\n\n    paths:\n      - { name: \"images\", dest: \"{{ retronas_path }}\", state: \"directory\", mode: \"0755\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\"}\n\n  tasks:\n\n    - name: \"{{ my_name }} - Include systems map\"\n      ansible.builtin.include_vars: \"retronas_systems.yml\"\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_litch.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"litch\"\n    my_file: \"install_litch\"\n    module_name: \"litch\"\n\n    packages:\n      - python3\n      - python3-bs4\n      - git\n\n    templates:\n      - { name: \"litch_login.sh\",          dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"litch_download.sh\",       dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"litch_download_clean.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"litch_claim.sh\",          dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - name: \"{{ my_name }} - create GOG dir\"\n      ansible.builtin.file:\n        path: \"{{ retronas_path }}/itchio\"\n        owner: \"{{ retronas_user }}\"\n        group: \"{{ retronas_group }}\"\n        mode: \"0775\"\n        state: directory\n\n    - name: \"{{ my_name }} - download litch\"\n      ansible.builtin.shell:\n        chdir: \"{{ retronas_root }}/bin\"\n        cmd: \"git clone https://github.com/sairuk/litch.git\"\n        creates: \"{{ retronas_root }}/bin/litch\"\n\n    - name: \"{{ my_name }} - update litch\"\n      ansible.builtin.shell:\n        chdir: \"{{ retronas_root }}/bin/litch\"\n        cmd: \"git pull\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_lynx.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"lynx\"\n    my_file: \"install_lynx\"\n    module_name: \"lynx\"\n\n    packages:\n      - lynx\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_macproxy_classic.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"macproxy_classic\"\n    my_file: \"install_macproxy_classic\"\n    module_name: \"macproxy_classic\"\n\n    packages:\n      - python3\n      - python3-venv\n      - python3-pip\n\n    service_name: \"macproxy\"\n\n    templates:\n      - { name: \"{{ service_name }}.service\", dest: \"/etc/systemd/system\" }\n\n    repo: https://github.com/rdmark/macproxy_classic.git\n\n    firewalld_ports:\n      - { port: 5001, protocol: tcp }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - name: \"{{ my_name }} - clone repo\"\n      ansible.builtin.git:\n        repo: \"{{ repo }}\"\n        dest: /opt/{{ my_name }}\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n    - name: \"{{ my_name }} Restart Services\"\n      ansible.builtin.service:\n        name: \"{{ service_name }}.service\"\n        state: started\n        enabled: true\n        daemon_reload: true\n"
  },
  {
    "path": "ansible/install_mc.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"mc\"\n    my_file: \"install_mc\"\n    module_name: \"mc\"\n\n    packages:\n      - mc\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"{{ my_name }} - Load RetroNAS systems\"\n      ansible.builtin.include_vars: retronas_systems.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_megatools.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: true\n\n  vars:\n    my_name: \"megatools\"\n    my_file: \"install_megatools\"\n    module_name: \"megatools\"\n\n    packages_build:\n      - build-essential\n      - libglib2.0-dev\n      - libssl-dev\n      - libcurl4-openssl-dev\n      - make\n      - gcc\n      - coreutils\n      - meson\n      - ninja-build\n\n    packages:\n      - megatools\n\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - block:\n      - name: \"{{ my_name }} - Install distro package\"\n        ansible.builtin.import_role:\n          name: retronas.role.package.latest\n      rescue:\n        - ansible.builtin.set_fact:\n            package_distro: false\n        - meta: clear_host_errors\n\n    - block:\n      - ansible.builtin.import_role:\n          name: retronas.role.package.latest\n        vars:\n          packages: \"{{ packages_build }}\"\n\n      - ansible.builtin.import_role:\n          name: retronas.role.templates\n\n      - name: \"{{ my_name }} - build source\"\n        ansible.builtin.command:\n          cmd: \"{{ retronas_root }}/scripts/{{ my_file }}.sh\"\n          creates: \"/usr/local/bin/megatools\"\n      when: not package_distro|default(true)\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_minicom.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"minicom\"\n    my_file: \"install_{{ my_name }}\"\n    my_dir: \"\"\n    module_name: \"minicom\"\n    append_user_group: \"dialout\"\n\n    packages:\n      - minicom\n      - lrzsz\n\n    templates:\n      - { name: \"minirc.dfl\", dest: \"/etc/minicom\", mode: \"0644\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_user }}\", force: no }\n      - { name: \"minicom.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\"}\n\n    script_links:\n      - { dest: \"minicom-config.sh\", src: \"minicom.sh\" }\n\n    paths:\n      - { name: \"{{ my_name }}\", dest: \"/etc\", state: \"directory\", mode: \"0755\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_user }}\" }\n      - { name: \"{{ my_name }}\", dest: \"{{ retronas_path }}\", state: \"directory\", mode: \"0755\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_user }}\"}\n\n  tasks:\n\n    - name: \"{{ my_name }} - Include systems map\"\n      ansible.builtin.include_vars: \"retronas_systems.yml\"\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - ansible.builtin.import_role:\n        name: retronas.role.update-user\n\n    - name: \"{{ my_name }} - script links\"\n      ansible.builtin.file:\n        dest: \"{{ retronas_root }}/scripts/{{ item.dest }}\"\n        src: \"{{ retronas_root }}/scripts/{{ item.src }}\"\n        state: link\n      with_items: \"{{ script_links }}\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_mister-organize.yml",
    "content": "---\n- hosts: localhost\n\n  vars:\n    my_name: \"MiSTer Organize\"\n    my_file: \"install_mister-organize\"\n    module_name: \"mister-organize\"\n\n    packages:\n      - git\n      - curl\n      - unzip\n\n    paths:\n      - { name: \"mister-organize\", dest: \"{{ retronas_path }}\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n      - { name: \"romimport\", dest: \"{{ retronas_path }}\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\", force: true}\n      - { name: \"mister-organize.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\"}\n\n  tasks:\n\n    - ansible.builtin.assert:\n        that: ansible_architecture == \"x86_64\"\n        fail_msg: \"Unsupported architecture\"\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"{{ my_name }} - Load RetroNAS Systems\"\n      ansible.builtin.include_vars: retronas_systems.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - Install from project repo\"\n      ansible.builtin.git:\n        repo: \"https://github.com/MiSTerOrganize/MiSTerOrganize.git\"\n        dest: \"{{ retronas_path}}/{{ module_name }}\"\n        single_branch: true\n        clone: true\n        update: true\n        force: true\n\n    - name: \"{{ my_name }} - setup local\"\n      ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba.system\n      vars:\n        system_key: \"{{ module_name }}\"\n"
  },
  {
    "path": "ansible/install_mister_cifs.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"MiSTer CIFS\"\n    my_file: \"install_mister_cifs\"\n    module_name: \"mister_cifs\"\n    system_key: \"mister\"\n\n    top_level_paths:\n      - { name: \"games\",      enabled: yes,  generic: \"roms\",        systems: yes }\n      - { name: \"saves\",      enabled: yes,  generic: \"saves\",       systems: yes }\n      - { name: \"savestates\", enabled: yes,  generic: \"savestates\",  systems: yes }\n      - { name: \"BIOS\",       enabled: yes,  generic: \"bios\",        systems: yes }\n      - { name: \"wallpapers\", enabled: yes,  generic: \"wallpapers\",  systems: no  }\n\n    save_overrides:\n      - { name: \"GAMEBOY2P\", src: \"nintendo/gameboy2p\" }\n      - { name: \"GBA2P\", src: \"nintendo/gameboyadvance2p\" }\n\n    templates:\n      #  - { name: \"retronas_mister.conf\", dest: \"/etc/samba\" }\n      - { name: \"retronas-mister-dirs.service\", dest: \"/etc/systemd/system\" }\n      - { name: \"retronas-mister-dirs.timer\", dest: \"/etc/systemd/system\" }\n\n    systemd_units:\n      - { name: \"retronas-mister-dirs\", type: 'service', state: \"stopped\", enabled: \"no\", restart: \"no\", instance: \"no\" }\n      - { name: \"retronas-mister-dirs\", type: 'timer',   state: \"stopped\", enabled: \"no\", restart: \"no\", instance: \"no\" }\n\n    dirs_only: false\n    savedirs:\n      - \"saves\"\n      - \"savestates\"\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.extradirs\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba.system\n\n    - name: \"{{ my_name }} - create custom save directories\"\n      ansible.builtin.file:\n        dest: \"{{ retronas_path }}/{{ item.1 }}/{{ item.0.src|lower }}\"\n        state: directory\n      loop: \"{{ save_overrides|product(savedirs)|list }}\"\n\n    - name: \"{{ my_name }} - create custom save links\"\n      ansible.builtin.file:\n        src: \"../../{{ item.1 }}/{{ item.0.src|lower }}\"\n        dest: \"{{ retronas_path }}/{{ system_key }}/{{ item.1 }}/{{ item.0.name }}\"\n        state: link\n      loop: \"{{ save_overrides|product(savedirs)|list }}\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n      when: dirs_only is false\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} daemon-reload\"\n      ansible.builtin.systemd:\n        daemon_reload: true\n"
  },
  {
    "path": "ansible/install_mtcp-netdrive.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: true\n\n  vars:\n    my_name: \"mtcp-netdrive\"\n    my_file: \"install_mtcp-netdrive\"\n    module_name: \"mtcp-netdrive\"\n\n    packages:\n      - screen\n      - curl\n      - unzip\n\n    paths:\n      - { name: \"{{ my_name }}\", dest: \"/opt\", state: \"directory\", mode: \"0755\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n\n    templates:\n      - { name: \"install_mtcp-netdrive.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\", force: \"yes\" }\n      - { name: \"mtcp-netdrive.service\", dest: \"/etc/systemd/system\", force: \"yes\" }\n      - { name: \"mtcp-netdrive.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\", force: \"yes\" }\n\n    firewalld_rules:\n      - { zones: retro, service: mtcp-netdrive }\n\n    my_services:\n      - mtcp-netdrive\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - install mtcp-netdrive\"\n      ansible.builtin.command:\n        cmd: \"{{ retronas_root }}/scripts/{{ my_file }}.sh\"\n\n    - name: \"{{ my_name }} - enable startup services\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: started\n        enabled: true\n        daemon-reload: true\n      with_items: \"{{ my_services }}\"\n\n    #\n    # FIREWALL\n    #\n    - name: \"{{ my_name }} - checking firewall rule\"\n      ansible.builtin.stat:\n        path: /etc/firewalld/services\n      register: firewalld_services\n\n    - name: \"{{ my_name }} - Generate firewall service\"\n      ansible.builtin.template:\n        src: \"templates/{{ my_file }}/mtcp-netdrive.xml.j2\"\n        dest: \"/etc/firewalld/services/mtcp-netdrive.xml\"\n        owner: root\n        group: root\n        mode: 0644\n      when: firewalld_services.stat.exists\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_mysticbbs.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: true\n\n  vars:\n    my_name: \"mysticbbs\"\n    my_file: \"install_mysticbbs\"\n    module_name: \"mysticbbs\"\n\n    packages:\n      - p7zip-full\n      - p7zip-rar\n      - screen\n\n    paths:\n      - { name: \"{{ my_name }}\", dest: \"/opt\", state: \"directory\", mode: \"0755\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n\n    templates:\n      - { name: \"mysticbbs.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\", force: \"yes\" }\n      - { name: \"install_mysticbbs.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\", force: \"yes\" }\n      - { name: \"mysticbbs-mis.service\", dest: \"/etc/systemd/system\", force: \"yes\" }\n      - { name: \"retronas_create_fileareas.ini\", dest: \"/opt/mysticbbs\", force: \"yes\" }\n      - { name: \"retronas_massupload.ini\", dest: \"/opt/mysticbbs\", force: \"yes\" }\n      - { name: \"create_filebone_na.py\", dest: \"/opt/mysticbbs\", force: \"yes\" }\n\n    firewalld_rules:\n      - { zones: retro, service: mysticbbs }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"{{ my_name }} - Enable non-free repo for p7zip-rar\"\n      ansible.builtin.apt_repository:\n        repo: deb http://deb.debian.org/debian/ {{ ansible_distribution_release }} main non-free-firmware non-free\n        state: present\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - install mysticbbs\"\n      ansible.builtin.command:\n        cmd: \"{{ retronas_root }}/scripts/{{ my_file }}.sh\"\n\n    - name: \"{{ my_name }} - daemon-reload\"\n      ansible.builtin.command:\n        cmd: \"systemctl daemon-reload\"\n\n    #\n    # FIREWALL\n    #\n    - name: \"{{ my_name }} - checking firewall rule\"\n      ansible.builtin.stat:\n        path: /etc/firewalld/services\n      register: firewalld_services\n\n    - name: \"{{ my_name }} - Generate firewall service\"\n      ansible.builtin.template:\n        src: \"templates/{{ my_file }}/mysticbbs.xml.j2\"\n        dest: \"/etc/firewalld/services/mysticbbs.xml\"\n        owner: root\n        group: root\n        mode: 0644\n      when: firewalld_services.stat.exists\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_nabu.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"nabu\"\n    my_file: \"install_{{ my_name }}\"\n    my_dir: /opt/nabu\n    module_name: \"nabu\"\n\n    systemd_units:\n      - { name: \"nabu\", type: 'service', state: \"started\", enabled: \"yes\", restart: \"yes\", instance: \"no\" }\n\n    packages:\n      - unzip\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"{{ my_name }}.sh\", dest: \"{{ retronas_root }}/scripts\",  mode: \"0755\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n\n    links:\n      - { dest: \"/home/{{ retronas_user }}/NABU Internet Adapter\", src: \"{{ retronas_path }}/roms/nabu\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Include systems map\"\n      ansible.builtin.include_vars: \"retronas_systems.yml\"\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - name: \"{{ my_name }} - add {{ retronas_user }} to dialout group\"\n      ansible.builtin.user:\n        name: \"{{ retronas_user }}\"\n        group: \"dialout\"\n        append: true\n\n    - name: \"{{ my_name }} - links\"\n      ansible.builtin.file:\n        src: \"{{ item.src }}\"\n        dest: \"{{ item.dest }}\"\n        state: \"{{ item.state|default('link') }}\"\n      with_items: \"{{ links }}\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - Install\"\n      ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n      args:\n        creates: \"{{ retronas_root}} {{ my_dir }}/nabu.sh\"\n"
  },
  {
    "path": "ansible/install_nbd-client.yml",
    "content": "---\n- hosts: localhost\n\n  vars:\n    my_name: \"nbd-client\"\n    my_file: \"install_nbd-client\"\n    module_name: \"nbd-client\"\n\n    packages:\n      - nbd-client\n\n    templates:\n      - { name: \"nbd.conf\", dest: \"/etc/modules-load.d/\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - enable ndb kernel module\"\n      ansible.builtin.shell:\n        cmd: \"modprobe nbd\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_netatalk2.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: true\n\n  vars:\n    my_name: \"Netatalk2\"\n    my_file: \"install_netatalk2\"\n\n    packages:\n      - libacl1\n      - libattr1\n      - libavahi-client3\n      - libavahi-common3\n      - libc6\n      - libcomerr2\n      - libcrack2\n      - libcups2\n      - libdb5.3\n      - libgcrypt20\n      - libgssapi-krb5-2\n      - libk5crypto3\n      - libkrb5-3\n      - libldap-2.4-2\n      - libpam0g\n      - libpam-modules\n      - libwrap0\n      - netbase\n      - perl\n      - aria2\n      - avahi-daemon\n\n  tasks:\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.debug:\n        msg: \"No longer supported, use Netatalk 4\"\n\n    - ansible.builtin.assert:\n        that: true == false\n\n    - name: \"{{ my_name }} - unhold netatalk package\"\n      ansible.builtin.shell: \"/usr/bin/apt-mark unhold netatalk\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - name: \"{{ my_name }} - download package for arm64\"\n      ansible.builtin.shell:\n        chdir: \"/tmp\"\n        cmd: \"aria2c --allow-overwrite=true http://mirror.aarnet.edu.au/pub/debian/pool/main/n/netatalk/netatalk_2.2.5-2+deb9u1_arm64.deb http://ftp.debian.org/debian/pool/main/n/netatalk/netatalk_2.2.5-2+deb9u1_arm64.deb\"\n      when: ansible_architecture == \"aarch64\"\n\n    - name: \"{{ my_name }} - download package for armhf\"\n      ansible.builtin.shell:\n        chdir: \"/tmp\"\n        cmd: \"aria2c --allow-overwrite=true http://mirror.aarnet.edu.au/pub/debian/pool/main/n/netatalk/netatalk_2.2.5-2+deb9u1_armhf.deb http://ftp.debian.org/debian/pool/main/n/netatalk/netatalk_2.2.5-2+deb9u1_armhf.deb\"\n      when: ansible_architecture == \"armhf\"\n\n    - name: \"{{ my_name }} - download package for x86_64\"\n      ansible.builtin.shell:\n        chdir: \"/tmp\"\n        cmd: \"aria2c --allow-overwrite=true http://mirror.aarnet.edu.au/pub/debian/pool/main/n/netatalk/netatalk_2.2.5-2+deb9u1_amd64.deb http://ftp.debian.org/debian/pool/main/n/netatalk/netatalk_2.2.5-2+deb9u1_amd64.deb\"\n      when: ansible_architecture == \"x86_64\"\n\n    - name: \"{{ my_name }} - download package for x86\"\n      ansible.builtin.shell:\n        chdir: \"/tmp\"\n        cmd: \"aria2c --allow-overwrite=true http://mirror.aarnet.edu.au/pub/debian/pool/main/n/netatalk/netatalk_2.2.5-2+deb9u1_i386.deb http://ftp.debian.org/debian/pool/main/n/netatalk/netatalk_2.2.5-2+deb9u1_i386.deb\"\n      when: ansible_architecture == \"i386\"\n\n    - name: \"{{ my_name }} - install package\"\n      ansible.builtin.shell:\n        chdir: \"/tmp\"\n        cmd: \"dpkg -i netatalk_2.2.5*.deb\"\n\n    - name: \"{{ my_name }}  - hold netatalk package\"\n      ansible.builtin.shell: \"/usr/bin/apt-mark hold netatalk\"\n\n    - name: \"{{ my_name }} - configure /etc/default/netatalk\"\n      ansible.builtin.template:\n        src: \"templates/{{ my_file }}/default.j2\"\n        dest: /etc/default/netatalk\n        owner: root\n        group: root\n        mode: '0644'\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - configure AppleVolumes.default\"\n      ansible.builtin.template:\n        src: \"templates/{{ my_file }}/AppleVolumes.default.j2\"\n        dest: /etc/netatalk/AppleVolumes.default\n        owner: root\n        group: root\n        mode: '0644'\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - configure afpd.conf\"\n      ansible.builtin.template:\n        src: \"templates/{{ my_file }}/afpd.conf.j2\"\n        dest: /etc/netatalk/afpd.conf\n        owner: root\n        group: root\n        mode: '0644'\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - enable startup services\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: started\n        enabled: true\n      with_items:\n        - avahi-daemon\n        - netatalk\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: restarted\n      with_items:\n        - avahi-daemon\n        - netatalk\n\n- ansible.builtin.import_playbook: retronas_system_config.yml\n  vars:\n    module_name: \"netatalk2\"\n    module_state: \"present\"\n"
  },
  {
    "path": "ansible/install_netatalk2x.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: true\n\n  vars:\n    my_name: \"Netatalk2.X\"\n    my_file: \"install_netatalk2x\"\n\n    packages:\n      - avahi-daemon\n      - build-essential\n      - make\n      - automake\n      - autoconf\n      - libdb-dev\n      - libdb++-dev\n      - libavahi-common-dev\n      - libavahi-client-dev\n      - libavahi-core-dev\n      - libdbus-1-dev\n      - libssl-dev\n      - autotools-dev\n      - libtool\n      - libcups2-dev\n      - libavahi-client-dev\n      - libgcrypt20-dev\n      - expect\n      - libltdl-dev\n      - libtool-bin\n      - libevent-dev\n\n    services:\n      - avahi-daemon\n      - atalkd\n      - afpd\n      - papd\n      - timelord\n      - a2boot\n\n    templates:\n      - { name: \"afpexpect.sh\", dest: \"{{ retronas_root }}/bin/netatalk2x/bin\", mode: \"0754\" }\n      - { name: \"AppleVolumes.default\", dest: \"{{ retronas_root }}/bin/netatalk2x/etc/netatalk\" }\n      - { name: \"afpd.conf\", dest: \"{{ retronas_root }}/bin/netatalk2x/etc/netatalk\" }\n      - { name: \"atalkd.conf\", dest: \"{{ retronas_root }}/bin/netatalk2x/etc/netatalk\" }\n\n  tasks:\n\n    - name: \"{{ my_name }}  - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"{{ my_name }}  - check if  netatalk package is available\"\n      ansible.builtin.shell: \"/usr/bin/apt-cache search netatalk | grep netatalk\"\n      register: result\n      failed_when:\n        - result.rc >= 2\n      when:\n        - ansible_distribution == \"Debian\"\n\n    - block:\n      - name: \"{{ my_name }}  - unhold netatalk package\"\n        ansible.builtin.shell: \"/usr/bin/apt-mark unhold netatalk\"\n\n      - name: \"{{ my_name }}  - remove package-based Netatalk\"\n        ansible.builtin.package:\n          name: netatalk\n          state: absent\n\n      - ansible.builtin.import_role:\n          name: retronas.role.package.latest\n\n      - name: \"{{ my_name }} - create install script\"\n        ansible.builtin.template:\n          src: \"templates/{{ my_file }}/{{ my_file }}.sh.j2\"\n          dest: \"{{ retronas_root }}/scripts/{{ my_file }}.sh\"\n          owner: root\n          group: root\n          mode: '0755'\n\n      - name: \"{{ my_name }} - install from source\"\n        ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n\n      - name: \"{{ my_name }} - templates\"\n        ansible.builtin.template:\n          src: \"templates/{{ my_file }}/{{ item.name }}.j2\"\n          dest: \"{{ item.dest }}/{{ item.name }}\"\n          owner: \"{{ item.owner|default('root') }}\"\n          group: \"{{ item.group|default('root') }}\"\n          mode: \"{{ item.mode|default('0644') }}\"\n        with_items: \"{{ templates }}\"\n        notify: \"{{ my_name }} - Restart service\"\n\n      - name: \"{{ my_name }} - enable startup services\"\n        ansible.builtin.service:\n          name: \"{{ item }}\"\n          state: started\n          enabled: true\n        with_items: \"{{ services }}\"\n      when: \n        - result is defined\n        - result.rc == 0\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: restarted\n      with_items: \"{{ services }}\"\n\n- ansible.builtin.import_playbook: retronas_system_config.yml\n  vars:\n    module_name: \"netatalk2x\"\n    module_state: \"present\"\n"
  },
  {
    "path": "ansible/install_netatalk3.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"Netatalk3\"\n    my_file: \"install_netatalk3\"\n\n    packages:\n      - avahi-daemon\n      - netatalk\n\n    my_services:\n      - avahi-daemon\n      - netatalk\n\n    config_settings:\n      - { section: \"Global\", option: \"uam list\", value: \"uams_guest.so uams_clrtxt.so uams_dhx.so uams_dhx2.so\" }\n      - { section: \"Global\", option: \"hostname\", value: \"retroafp\" }\n      - { section: \"Global\", option: \"mimic model\", value: \"PowerMac\" }\n      - { section: \"Global\", option: \"zeroconf\", value: \"yes\" }\n      - { section: \"Global\", option: \"log level\", value: \"info\" }\n      - { section: \"Global\", option: \"log file\", value: \"/var/log/afp.log\" }\n      - { section: \"Global\", option: \"afp listen\", value: \"0.0.0.0\" }\n      - { section: \"Global\", option: \"include\", value: \"/etc/netatalk/retronas.conf\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"{{ my_name }} - check if  netatalk package is available\"\n      ansible.builtin.shell: \"/usr/bin/apt-cache search netatalk | grep netatalk\"\n      register: result\n      failed_when:\n        - result.rc >= 2\n\n    - name: \"{{ my_name }} - set fact if netatalk is not present\"\n      ansible.builtin.set_fact:\n        no_netatalk: true\n      when: result.rc == 1\n\n    - name: \"{{ my_name }} - end play if no netatalk package\"\n      ansible.builtin.meta: end_play\n      when: no_netatalk is defined and no_netatalk is true\n\n    - name: \"{{ my_name }}  - unhold netatalk package\"\n      ansible.builtin.shell: \"/usr/bin/apt-mark unhold netatalk\"\n      when: result.rc == 0\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - name: \"{{ my_name }}  - configure\"\n      ansible.builtin.ini_file:\n        dest: /etc/netatalk/afp.conf\n        section: \"{{ item.section }}\"\n        option: \"{{ item.option }}\"\n        value: \"{{ item.value }}\"\n      with_items: \"{{ config_settings }}\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - configure retro shares\"\n      ansible.builtin.template:\n        src: \"templates/{{ my_file }}/retronas.conf.j2\"\n        dest: /etc/netatalk/retronas.conf\n        owner: root\n        group: root\n        mode: '0644'\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - enable startup services\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: started\n        enabled: true\n      with_items: \"{{ my_services }}\"\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: restarted\n      with_items: \"{{ my_services }}\"\n\n- ansible.builtin.import_playbook: retronas_system_config.yml\n  vars:\n    module_name: \"netatalk3\"\n    module_state: \"present\"\n  when: no_netatalk is undefined\n"
  },
  {
    "path": "ansible/install_netatalk3_source.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: true\n\n  vars:\n    my_name: \"Netatalk3\"\n    my_file: \"install_netatalk3\"\n\n    packages:\n      - avahi-daemon\n      - build-essential\n      - libevent-dev\n      - libssl-dev\n      - libgcrypt-dev\n      - libkrb5-dev\n      - libpam0g-dev\n      - libwrap0-dev\n      - libdb-dev\n      - libtdb-dev\n      - libavahi-client-dev\n      - libacl1-dev\n      - libldap2-dev\n      - libcrack2-dev\n      - systemtap-sdt-dev\n      - libdbus-1-dev\n      - libdbus-glib-1-dev\n      - libglib2.0-dev\n      - libio-socket-inet6-perl\n      - tracker\n\n    packages_debian:\n      debian11:\n        - libmysqlclient-dev\n        - libtracker-sparql-2.0-dev\n      debian12:\n        - default-libmysqlclient-dev\n        - libtracker-sparql-3.0-dev\n\n    packages_ubuntu:\n      - libmysqlclient-dev\n      - libtracker-sparql-2.0-dev\n      - libtracker-miner-2.0-dev\n\n\n    my_services:\n      - avahi-daemon\n      - netatalk\n\n    config_settings:\n      - { section: \"Global\", option: \"uam list\", value: \"uams_guest.so uams_clrtxt.so uams_dhx.so uams_dhx2.so\" }\n      - { section: \"Global\", option: \"hostname\", value: \"retroafp\" }\n      - { section: \"Global\", option: \"mimic model\", value: \"PowerMac\" }\n      - { section: \"Global\", option: \"zeroconf\", value: \"yes\" }\n      - { section: \"Global\", option: \"log level\", value: \"info\" }\n      - { section: \"Global\", option: \"log file\", value: \"/var/log/afp.log\" }\n      - { section: \"Global\", option: \"afp listen\", value: \"0.0.0.0\" }\n      - { section: \"Global\", option: \"include\", value: \"/etc/netatalk/retronas.conf\" }\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts/\", mode: '0755'}\n      - { name: \"netatalk.service\", dest: \"/etc/systemd/system\"}\n\n\n  tasks:\n\n    - name: \"{{ my_name }}  - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"{{ my_name }}  - check if  netatalk package is available\"\n      ansible.builtin.shell: \"/usr/bin/apt-cache search netatalk | grep netatalk\"\n      register: result\n      failed_when:\n        - result.rc >= 2\n\n    - name: \"{{ my_name }} - unhold netatalk package\"\n      ansible.builtin.shell: \"/usr/bin/apt-mark unhold netatalk\"\n      when: result.rc == 0\n\n    - name: \"{{ my_name }}  - remove package-based Netatalk\"\n      ansible.builtin.package:\n        name: netatalk\n        state: absent\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - name: \"{{ my_name }} - Install build tools (debian)\"\n      ansible.builtin.package:\n        name: \"{{ packages_debian['debian' + ansible_distribution_major_version ] }}\"\n        state: latest\n      when: ansible_distribution == 'Debian'\n\n    - name: \"{{ my_name }} - Install build tools (ubuntu)\"\n      ansible.builtin.package:  \n        name: \"{{ packages_ubuntu }}\"\n        state: latest\n      when: ansible_distribution == 'Ubuntu'\n\n    - name: \"{{ my_name }} - templates\"\n      ansible.builtin.template:\n        src: \"templates/{{ my_file }}/{{ item.name }}.j2\"\n        dest: \"{{ item.dest }}/{{ item.name }}\"\n        owner: \"{{ item.owner|default('root') }}\"\n        group: \"{{ item.group|default('root') }}\"\n        mode: \"{{ item.mode|default('0644') }}\"\n      with_items: \"{{ templates }}\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - install from source\"\n      ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n\n    - name: \"{{ my_name }} - configure\"\n      ansible.builtin.ini_file:\n        dest: /etc/netatalk/afp.conf\n        section: \"{{ item.section }}\"\n        option: \"{{ item.option }}\"\n        value: \"{{ item.value }}\"\n      with_items: \"{{ config_settings }}\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - configure retro shares\"\n      ansible.builtin.template:\n        src: \"templates/{{ my_file }}/retronas.conf.j2\"\n        dest: /etc/netatalk/retronas.conf\n        owner: root\n        group: root\n        mode: '0644'\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - enable startup services\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: started\n        enabled: true\n      with_items: \"{{ my_services }}\"\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: restarted\n        daemon-reload: true\n      with_items: \"{{ my_services }}\"\n\n- ansible.builtin.import_playbook: retronas_system_config.yml\n  vars:\n    module_name: \"netatalk3\"\n    module_state: \"present\"\n"
  },
  {
    "path": "ansible/install_netatalk4.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: true\n\n  vars:\n    my_name: \"Netatalk4\"\n    my_file: \"install_netatalk4\"\n    module_name: \"netatalk4\"\n\n    packages:\n      - avahi-daemon\n      - avahi-utils\n      - bison\n      - build-essential\n      - cmark\n      - flex\n      - libacl1-dev\n      - libavahi-client-dev\n      - libcmark-dev\n      - libcrack2-dev\n      - libdb-dev\n      - libdbus-1-dev\n      - libdbus-glib-1-dev\n      - libevent-dev\n      - libgcrypt-dev\n      - libglib2.0-dev\n      - libiniparser-dev\n      - libio-socket-inet6-perl\n      - libkrb5-dev\n      - libldap2-dev\n      - libmptcpwrap0\n      - libpam0g-dev\n      - libssl-dev\n      - libtdb-dev\n      - libtirpc-dev\n      - libtirpc3\n      - libwrap0-dev\n      - meson\n      - ninja-build\n      - po4a\n      - systemtap-sdt-dev\n      - tracker\n\n    packages_debian:\n      debian11:\n        - libmysqlclient-dev\n        - libtracker-sparql-2.0-dev\n      debian12:\n        - default-libmysqlclient-dev\n        - libtracker-sparql-3.0-dev\n      debian13:\n        - default-libmysqlclient-dev\n        - libtracker-sparql-3.0-dev\n\n\n    packages_ubuntu:\n      - libmysqlclient-dev\n      - libtracker-sparql-2.0-dev\n      - libtracker-miner-2.0-dev\n\n    my_services:\n      - avahi-daemon\n      - atalkd\n      - netatalk\n\n    afp_settings:\n      - { section: \"Global\", option: \"uam list\", value: \"uams_guest.so uams_clrtxt.so uams_dhx.so uams_dhx2.so\" }\n      - { section: \"Global\", option: \"hostname\", value: \"retroafp\" }\n      - { section: \"Global\", option: \"mimic model\", value: \"PowerMac\" }\n      - { section: \"Global\", option: \"zeroconf\", value: \"yes\" }\n      - { section: \"Global\", option: \"log level\", value: \"info\" }\n      - { section: \"Global\", option: \"log file\", value: \"/var/log/afp.log\" }\n      - { section: \"Global\", option: \"afp listen\", value: \"0.0.0.0\" }\n      - { section: \"Global\", option: \"appletalk\", value: \"yes\" }\n      - { section: \"Global\", option: \"spotlight\", value: \"yes\" }\n      - { section: \"Global\", option: \"legacy icon\", value: \"globe\" }\n    #  - { section: \"Global\", option: \"include\", value: \"/opt/retronas/bin/netatalk4/etc/retronas.conf\" }\n      - { section: \"retronas\", option: \"path\", value: \"{{ retronas_path }}\" }\n      - { section: \"retronas\", option: \"rwlist\", value: \"{{ retronas_user }}\" }\n      - { section: \"retronas\", option: \"rolist\", value: \"guest\" }\n      - { section: \"retronas\", option: \"follow symlinks\", value: \"yes\" }\n\n    paths:\n      - { name: \"netatalk4\", dest: \"/opt/retronas/bin\", mode: '0755'  }\n      - { name: \"etc\", dest: \"/opt/retronas/bin/netatalk4\", mode: '0755'  }\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts/\", mode: '0755' }\n    #  - { name: \"retronas.conf\", dest: \"/opt/retronas/bin/netatalk4/etc\" }\n\n    old_services:\n      - { name: \"atalkd.service\", dest: \"/etc/systemd/system\"}\n      - { name: \"netatalk.service\", dest: \"/etc/systemd/system\"}\n\n  tasks:\n\n    - name: \"{{ my_name }}  - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"{{ my_name }}  - check if netatalk package is available\"\n      ansible.builtin.shell: \"/usr/bin/apt-cache search netatalk | grep netatalk\"\n      register: result\n      failed_when:\n        - result.rc >= 2\n\n    - name: \"{{ my_name }} - unhold netatalk package\"\n      ansible.builtin.shell: \"/usr/bin/apt-mark unhold netatalk\"\n      when: result.rc == 0\n\n    - name: \"{{ my_name }} - remove package-based Netatalk\"\n      ansible.builtin.package:\n        name: netatalk\n        state: absent\n\n    - name: \"{{ my_name }} - remove old service files\"\n      ansible.builtin.stat:\n        path: \"{{ item.dest }}/{{ item.name }}\"\n      loop: \"{{ old_services }}\"\n      register: old_services_check\n\n    - name: \"{{ my_name }} - Stopping existing netatalk service(s) during installation\"\n      ansible.builtin.service:\n        name: \"{{ item.stat.path }}\"\n        state: stopped\n        daemon-reload: true\n      loop: \"{{ old_services_check.results }}\"\n      when: old_services_check is defined and\n            item.stat.exists is true\n\n    - name: \"{{ my_name }} - Remove old service files\"\n      ansible.builtin.file:\n        path: \"{{ item.stat.path }}\"\n        state: absent\n      loop: \"{{ old_services_check.results }}\"\n      when: old_services_check is defined and\n            item.stat.exists is true\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - name: \"{{ my_name }} - Install build tools (debian)\"\n      ansible.builtin.package:\n        name: \"{{ packages_debian['debian' + ansible_distribution_major_version ] }}\"\n        state: latest\n      when: ansible_distribution == 'Debian'\n\n    - name: \"{{ my_name }} - Install build tools (ubuntu)\"\n      ansible.builtin.package:\n        name: \"{{ packages_ubuntu }}\"\n        state: latest\n      when: ansible_distribution == 'Ubuntu'\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - install from source\"\n      ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n\n    - name: \"{{ my_name }} - configure\"\n      ansible.builtin.ini_file:\n        dest: /opt/retronas/bin/netatalk4/etc/afp.conf\n        section: \"{{ item.section }}\"\n        option: \"{{ item.option }}\"\n        value: \"{{ item.value }}\"\n      with_items: \"{{ afp_settings }}\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - atalkd\"\n      ansible.builtin.lineinfile:\n        path: /opt/retronas/bin/netatalk4/etc/atalkd.conf\n        regexp: \"^{{ ansible_default_ipv4.interface }}.*\"\n        line: \"{{ ansible_default_ipv4.interface }} -router -phase 2 -net 1 -zone \\\"retroafp\\\"\"\n\n    - name: \"{{ my_name }} - enable startup services\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: started\n        enabled: true\n      with_items: \"{{ my_services }}\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: restarted\n        daemon-reload: true\n      with_items: \"{{ my_services }}\"\n"
  },
  {
    "path": "ansible/install_netlink.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"netlink\"\n    my_file: \"install_netlink\"\n    module_name: \"netlink\"\n\n    packages:\n      - curl\n      - unzip\n      - dnsmasq\n      - dnsutils\n      - libnetfilter-queue-dev\n      - libnetfilter-queue1\n      - ppp\n      - arping\n      - nftables\n      - tcpd\n      - wvdial\n      - python3-pip\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"{{ my_name }}.service\", dest: \"/etc/systemd/system\" }\n      - { name: \"{{ my_name }}.conf\", dest: \"/etc/dnsmasq.d\" }\n      - { name: \"{{ my_name }}.patch\", dest: \"/tmp\" }\n\n    paths:\n      - { name: \"dnsmasq.d\", dest: \"/etc\" }\n\n    firewalld_ports:\n      - { zone: modern, port: 65432, protocol: tcp }\n      - { zone: modern, port: 20001, protocol: udp }\n      - { zone: modern, port: 20002, protocol: udp }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - Run installer\"\n      ansible.builtin.shell:\n        cmd: \"./{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n        chdir: \"{{ retronas_root }}/scripts\"\n        executable: /bin/bash\n      notify: \"{{ my_name }} Restart Services\"\n\n    - name: \"{{ my_name }} - Patch com port range detection\"\n      ansible.builtin.patch:\n        src: /tmp/{{ my_name }}.patch\n        dest: /opt/{{ my_name }}/tunnel.py\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n    - name: \"{{ my_name }} Restart Services\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: started\n        enabled: true\n        daemon_reload: true\n      with_items:\n        - \"{{ my_name }}.service\"\n"
  },
  {
    "path": "ansible/install_netmount.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: true\n\n  vars:\n    my_name: \"netmount\"\n    my_file: \"install_netmount\"\n    module_name: \"netmount\"\n\n    packages:\n      - bzip2\n      - python3\n      - python3-yaml\n\n    systemd_units:\n      - { name: \"netmount-drives-retronas\", type: 'service', state: \"started\", enabled: \"yes\", restart: \"yes\", instance: \"no\" }\n\n    templates:\n      - { name: \"{{ my_file }}.sh\",         dest: \"{{ retronas_root }}/scripts\",         mode: \"0755\" }\n      - { name: \"netmount-confman.py\",      dest: \"{{ retronas_root }}/scripts\",         mode: \"0755\" }\n      - { name: \"netmount-confman.sh\",      dest: \"{{ retronas_root }}/scripts\",         mode: \"0755\" }\n      - { name: \"retronas.yaml\",            dest: \"{{ retronas_path }}/config/netmount\", mode: \"0755\" }\n\n    firewalld_rules:\n      - { zones: retro, service: netmount }\n\n    paths:\n        - dos\n        - config/netmount\n\n  tasks:\n\n    - name: \"{{ my_name }} - Include systems map\"\n      ansible.builtin.include_vars: \"retronas_systems.yml\"\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - name: \"{{ my_name }} - build top level\"\n      ansible.builtin.file:\n        path: \"{{ retronas_path }}/{{ item }}\"\n        owner: \"{{ retronas_user }}\"\n        group: \"{{ retronas_group }}\"\n        state: directory\n        mode: \"0775\"\n      loop: \"{{ paths }}\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - Install from git\"\n      ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n      args:\n        creates: \"/opt/netmount/netmount\"\n\n    - name: \"{{ my_name }} - Build service files\"\n      ansible.builtin.shell: \"python3 {{ retronas_root }}/scripts/netmount-confman.py\"\n\n    - name: \"{{ my_name }} - enable service(s)\"\n      ansible.builtin.service:\n        name: \"{{ item.name }}.{{ item.type }}\"\n        state: \"{{ item.state }}\"\n        enabled: \"{{ item.enabled }}\"\n        daemon_reload: true\n      with_items: \"{{ systemd_units }}\"\n      when:\n        - item.instance == 'no'\n      notify: \"{{ my_name }} - Restart service\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item.name }}.{{ item.type }}\"\n        state: restarted\n        daemon_reload: true\n      with_items: \"{{ systemd_units }}\"\n      when:\n        - item.restart == 'yes'\n        - item.instance == 'no'\n\n"
  },
  {
    "path": "ansible/install_network-presets-ethernet-dhcp.yml",
    "content": "---\n# networkmanager\n- ansible.builtin.import_playbook: install_networkmanager.yml\n\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"network-presets-standalone-ethernet-dhcp\"\n    my_file: \"install_{{ my_name }}\"\n\n    connections:\n      - { name: \"ethernet\", state: \"absent\" }  # remove it first because modules are a pain some times\n      - { name: \"wifi-retronas\", state: \"absent\" }\n      - { name: \"ethernet\", ipv4method: \"auto\", ifname: \"{{ retronas_net_retro_interface }}\", type: \"ethernet\" }\n\n    disable_services:\n      - dhcpcd\n      - dnsmasq-retro\n      - firewalld\n      - hostapd-retronas\n\n  tasks:\n\n    - name: \"{{ my_name }}  - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"config connections\"\n      community.general.nmcli:\n        type: \"{{ item.type | default('ethernet') }}\"\n        conn_name: \"{{ item.name }}\"\n        ip4: \"{{ item.ipv4addr | default(omit) }}\"\n        gw4: \"{{ item.ipv4gw | default(omit) }}\"\n        dns4: \"{{ item.ipv4dns | default(omit) }}\"\n        method4: \"{{ item.ipv4method | default('manual') }}\"\n        ifname: \"{{ item.ifname | default(omit) }}\"\n        route_metric4: \"{{ item.metric | default(omit) }}\"\n        state: \"{{ item.state| default('present') }}\"\n        autoconnect: true\n      with_items:\n        - '{{ connections }}'\n\n    - name: \"{{ my_name }} - check which services is installed\"\n      ansible.builtin.stat:\n        path: /usr/lib/systemd/system/{{ item }}.service\n      loop: \"{{ disable_services }}\"\n      register: services_installed\n\n    - name: \"{{ my_name }} - back to dhcp, turn off our services\"\n      ansible.builtin.service:\n        name: \"{{ item.item }}\"\n        state: stopped\n        enabled: false\n        force: true\n      loop: \"{{ services_installed.results }}\"\n      when: item.stat.exists is true\n\n# alt presets removed\n- ansible.builtin.import_playbook: retronas_system_config.yml\n  vars:\n    module_name: \"network-presets-standalone\"\n    module_state: \"absent\"\n\n- ansible.builtin.import_playbook: retronas_system_config.yml\n  vars:\n    module_name: \"network-presets-zoned\"\n    module_state: \"absent\"\n"
  },
  {
    "path": "ansible/install_network-presets-standalone.yml",
    "content": "---\n# networkmanager\n- ansible.builtin.import_playbook: install_networkmanager.yml\n\n# hostapd\n# using NM api mode now\n# - ansible.builtin.import_playbook: install_hostapd.yml\n\n# dnsmassq\n# IMPORTED IN DNSMASQ-RETRO - ansible.builtin.import_playbook: install_dnsmasq.yml\n- ansible.builtin.import_playbook: install_dnsmasq-retro.yml\n\n# ntp\n- ansible.builtin.import_playbook: install_ntp.yml\n\n# dhpcd\n# - ansible.builtin.import_playbook: install_dhcpcd.yml\n\n\n- hosts: localhost\n  gather_facts: true\n\n  vars:\n    my_name: \"network-presets-standalone\"\n    my_file: \"install_{{ my_name }}\"\n\n    templates:\n      - { name: \"dhcpcd.conf\",         dest: \"/etc\",  mode: \"0640\", force: \"yes\" }\n\n    connections:\n      - { name: \"ethernet\", state: \"absent\" }\n      - { name: \"wifi-retronas\", state: \"absent\" }\n\n    my_packages:\n      - pwgen\n\n  tasks:\n\n    - name: \"{{ my_name }}  - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"{{ my_name }} - Install packages\"\n      ansible.builtin.package:\n        name: \"{{ my_packages }}\"\n        state: latest\n\n    - name: \"{{ my_name }} - generate password\"\n      shell:\n        cmd: pwgen -s 12 1\n      no_log: true\n      changed_when: false\n      register: retronas_wifi_password\n\n    - name: \"{{ my_name }} - generate unique ssid\"\n      shell:\n        cmd: echo \"retronas-$(pwgen 8 -A -0 -B 1)\"\n      register: retronas_net_wifi_ssid_generated\n      changed_when: false\n      when: retronas_net_wifi_ssid == \"\"\n\n    - name: \"{{ my_name }} - config connections\"\n      community.general.nmcli:\n        type: \"{{ item.type | default('ethernet') }}\"\n        conn_name: \"{{ item.name }}\"\n        ip4: \"{{ item.ipv4addr | default(omit) }}\"\n        gw4: \"{{ item.ipv4gw | default(omit) }}\"\n        dns4: \"{{ item.ipv4dns | default(omit) }}\"\n        method4: \"{{ item.ipv4method | default('manual') }}\"\n        ifname: \"{{ item.ifname | default(omit) }}\"\n        route_metric4: \"{{ item.metric | default(omit) }}\"\n        state: \"{{ item.state| default('present') }}\"\n        autoconnect: true\n      with_items:\n        - '{{ connections }}'\n\n    - name: \"{{ my_name }} - config ethernet\"\n      community.general.nmcli:\n        type: \"ethernet\"\n        conn_name: \"ethernet\"\n        ifname: \"{{ retronas_net_retro_interface }}\"\n        ip4: \"{{ retronas_net_retro_ip }}/{{ retronas_net_retro_subnet }}\"\n        gw4: \"{{ retronas_net_retro_router }}\"\n        dns4: \"{{ retronas_net_retro_dns }}\"\n        method4: \"manual\"\n        state: \"present\"\n        autoconnect: true\n\n    - name: \"{{ my_name }} - configure wifi ap\"\n      community.general.nmcli:\n        type: \"wifi\"\n        conn_name: \"wifi-retronas\"\n        ifname: \"{{ retronas_net_wifi_interface }}\"\n        ssid: \"{{ retronas_net_wifi_ssid if retronas_net_wifi_ssid_generated == '' else retronas_net_wifi_ssid_generated.stdout }}\"\n        ip4: \"{{ retronas_net_wifi_ip }}/{{ retronas_net_wifi_subnet }}\"\n        # gw4: \"{{ retronas_net_wifi_router }}\"\n        never_default4: true\n        dns4: \"{{ retronas_net_wifi_dns }}\"\n        method4: \"manual\"\n        wifi_sec:\n          key-mgmt: wpa-psk\n          psk: \"{{ retronas_wifi_password.stdout }}\"\n        wifi:\n          mode: \"ap\"\n          channel: \"{{ retronas_net_wifi_channel }}\"\n          band: \"{{ retronas_net_wifi_hwmode }}\"\n        state: present\n        autoconnect: true\n\n    - name: \"{{ my_name }} - templates\"\n      ansible.builtin.template:\n        src: \"templates/{{ my_file }}/{{ item.name }}.j2\"\n        dest: \"{{ item.dest }}/{{ item.name }}\"\n        owner: \"{{ item.owner|default('root') }}\"\n        group: \"{{ item.group|default('root') }}\"\n        mode: \"{{ item.mode|default('0644') }}\"\n        force: \"{{ item.force|default('yes') }}\"\n      loop: \"{{ templates }}\"\n\n    - name: \"{{ my_name }} - check firewalld is installed\"\n      ansible.builtin.stat:\n        path: /usr/lib/systemd/system/firewalld.service\n      register: firewalld_installed\n\n    - name: \"{{ my_name }}  - everything is retro here so disabling the firewalld zoned config\"\n      ansible.builtin.service:\n        name: firewalld.service\n        state: stopped\n        enabled: false\n        force: true\n      when: firewalld_installed.stat.exists is true\n\n    - name: \"{{ my_name }} - check dhcpcd is installed\"\n      ansible.builtin.stat:\n        path: /usr/lib/systemd/system/dhcpcd.service\n      register: dhcpcd_installed\n\n    - name: \"{{ my_name }} - restart dhcpcd\"\n      ansible.builtin.service:\n        name: dhcpcd.service\n        state: stopped\n        enabled: false\n        force: true\n      when: dhcpcd_installed.stat.exists is true\n\n    - name: \"{{ my_name }} - check hostapd is installed\"\n      ansible.builtin.stat:\n        path: /usr/lib/systemd/system/hostapd.service\n      register: hostapd_installed\n\n    - name: \"{{ my_name }} - remove hostapd dnsmasq configuration\"\n      ansible.builtin.file:\n        path: /etc/dnsmasq.d/retro/hostapd-dnsmasq.conf\n        state: absent\n      when: hostapd_installed.stat.exists is true\n\n    - name: \"{{ my_name }} - stop hostapd\"\n      ansible.builtin.service:\n        name: hostapd-retronas.service\n        state: stopped\n        enabled: false\n        force: true\n      when: hostapd_installed.stat.exists is true\n\n    - name: \"{{ my_name }} - restart dnsmasq\"\n      ansible.builtin.service:\n        name: dnsmasq-retro.service\n        state: restarted\n        enabled: true\n        force: true\n\n# alt preset removed\n- ansible.builtin.import_playbook: retronas_system_config.yml\n  vars:\n    module_name: \"network-presets-zoned\"\n    module_state: \"absent\"\n\n# this preset added\n- ansible.builtin.import_playbook: retronas_system_config.yml\n  vars:\n    module_name: \"network-presets-standalone\"\n    module_state: \"present\"\n"
  },
  {
    "path": "ansible/install_network-presets-zoned.yml",
    "content": "---\n# networkmanager\n- ansible.builtin.import_playbook: install_networkmanager.yml\n\n# firewalld\n- ansible.builtin.import_playbook: install_firewalld.yml\n- ansible.builtin.import_playbook: install_firewalld-zones.yml\n\n# dnsmasq\n# IMPORTED IN DNSMASQ-RETRO - ansible.builtin.import_playbook: install_dnsmasq.yml\n- ansible.builtin.import_playbook: install_dnsmasq-retro.yml\n\n# ntp\n- ansible.builtin.import_playbook: install_ntp.yml\n\n# dhcpcd\n- ansible.builtin.import_playbook: install_dhcpcd.yml\n\n\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"network-presets-zoned\"\n    my_file: \"install_{{ my_name }}\"\n\n    templates:\n      - { name: \"dhcpcd.conf\", dest: \"/etc\", mode: \"0640\", force: \"yes\" }\n\n    connections:\n      - { name: \"ethernet\", state: \"absent\" }  # remove it first because modules are a pain some times\n      - { name: \"wifi-retronas\", state: \"absent\" }\n      - { name: \"ethernet\", ipv4addr: \"{{ retronas_net_retro_ip }}/{{ retronas_net_retro_subnet }}\", ipv4dns: \"{{ retronas_net_retro_dns }}\", ipv4method: \"manual\", ifname: \"{{ retronas_net_retro_interface }}\", type: \"ethernet\", ipv4never_default4: true }\n\n  tasks:\n\n    - name: \"{{ my_name }}  - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"config connections\"\n      community.general.nmcli:\n        type: \"{{ item.type | default('ethernet') }}\"\n        conn_name: \"{{ item.name }}\"\n        ip4: \"{{ item.ipv4addr | default(omit) }}\"\n        gw4: \"{{ item.ipv4gw | default(omit) }}\"\n        never_default4: \"{{ item.ipv4never_default4 | default(omit) }}\"\n        dns4: \"{{ item.ipv4dns | default(omit) }}\"\n        method4: \"{{ item.ipv4method | default('manual') }}\"\n        ifname: \"{{ item.ifname | default(omit) }}\"\n        route_metric4: \"{{ item.metric | default(omit) }}\"\n        state: \"{{ item.state| default('present') }}\"\n        autoconnect: true\n      with_items:\n        - '{{ connections }}'\n\n    - name: \"{{ my_name }} - check hostapd is installed\"\n      ansible.builtin.stat:\n        path: /usr/lib/systemd/system/hostapd.service\n      register: hostapd_installed\n\n    - name: \"{{ my_name }} - remove hostapd dnsmasq configuration\"\n      ansible.builtin.file:\n        path: /etc/dnsmasq.d/retro/hostapd-dnsmasq.conf\n        state: absent\n      when: hostapd_installed.stat.exists is true\n\n    - name: \"{{ my_name }} - stop hostapd\"\n      ansible.builtin.service:\n        name: hostapd-retronas.service\n        state: stopped\n        enabled: false\n        force: true\n      when: hostapd_installed.stat.exists is true\n\n    - name: \"{{ my_name }} - templates\"\n      ansible.builtin.template:\n        src: \"templates/{{ my_file }}/{{ item.name }}.j2\"\n        dest: \"{{ item.dest }}/{{ item.name }}\"\n        owner: \"{{ item.owner|default('root') }}\"\n        group: \"{{ item.group|default('root') }}\"\n        mode: \"{{ item.mode|default('0644') }}\"\n        force: \"{{ item.force|default('yes') }}\"\n      with_items: \"{{ templates }}\"\n\n    - name: \"{{ my_name }} - restart dhcpcd\"\n      ansible.builtin.service:\n        name: dhcpcd.service\n        state: restarted\n        enabled: true\n        force: true\n\n    - name: \"{{ my_name }} - restart dnsmasq\"\n      ansible.builtin.service:\n        name: dnsmasq-retro.service\n        state: restarted\n        enabled: true\n        force: true\n\n    - name: \"{{ my_name }} - start firewalld\"\n      ansible.builtin.service:\n        name: firewalld.service\n        state: restarted\n        enabled: true\n        force: true\n\n# alt preset removed\n- ansible.builtin.import_playbook: retronas_system_config.yml\n  vars:\n    module_name: \"network-presets-standalone\"\n    module_state: \"absent\"\n\n# this preset added\n- ansible.builtin.import_playbook: retronas_system_config.yml\n  vars:\n    module_name: \"network-presets-zoned\"\n    module_state: \"present\"\n"
  },
  {
    "path": "ansible/install_networkmanager.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"network manager\"\n    my_file: \"install_{{ my_name }}\"\n    my_services: \"NetworkManager.service\"\n\n    collections:\n      - { collection: community.general, creates: community/general/nmcli }\n\n  tasks:\n\n    - name: \"{{ my_name }} - does nothing\"\n      debug:\n        msg: \"currently does nothing\"\n"
  },
  {
    "path": "ansible/install_nfs.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    module_name: \"nfs\"\n\n  tasks:\n\n    - ansible.builtin.import_role:\n        name: retronas.role.nfs\n"
  },
  {
    "path": "ansible/install_nginx.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    module_name: \"nginx\"\n\n  roles:\n    - retronas.role.sslcert\n    - retronas.role.nginx\n    - retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_ntp.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"ntp\"\n    my_service: \"openntpd\"\n    my_file: \"install_ntp\"\n    module_name: \"ntp\"\n\n    packages:\n      - openntpd\n\n    remove_packages:\n      - ntp\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"{{ my_name }} - Load RetroNAS systems\"\n      ansible.builtin.include_vars: retronas_systems.yml\n\n    - name: \"{{ my_name }} - remove packages\"\n      ansible.builtin.package:\n        name: \"{{ remove_packages }}\"\n        state: absent\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - name: \"{{ my_name }} - listen\"\n      ansible.builtin.lineinfile:\n        path: \"/etc/openntpd/ntpd.conf\"\n        search_string: \"^listen on *$\"\n        insertafter: \"^# Addresses to listen on (ntpd does not listen by default)$\"\n        line: \"listen on *\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"openntpd.service\"\n        state: restarted\n        daemon_reload: true\n        enabled: true\n"
  },
  {
    "path": "ansible/install_open-iscsi.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"open-iscsi\"\n    my_file: \"install_{{ my_name }}\"\n    module_name: \"open-iscsi\"\n\n    packages:\n      - open-iscsi\n\n    my_services:\n      - open-iscsi\n\n    templates:\n      - { name: \"iscsi-manager-target-login.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - Enable startup services\"\n      ansible.builtin.service:\n        name: ssh\n        state: started\n        enabled: true\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_openssh.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"OpenSSH\"\n    module_name: \"openssh\"\n\n    packages:\n      - openssh-client\n      - openssh-server\n      - openssh-sftp-server\n\n    firewalld_rules:\n      - { zone: retro, service: ssh }\n      - { zone: modern, service: ssh }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n      notify: \"{{ my_name }} - Restart ssh\"\n\n    - name: \"{{ my_name }} - Enable startup services\"\n      ansible.builtin.service:\n        name: ssh\n        state: started\n        enabled: true\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart ssh\"\n      ansible.builtin.service:\n        name: ssh\n        state: restarted\n"
  },
  {
    "path": "ansible/install_pandoc.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    - my_name: \"pandoc\"\n    - my_file: \"install_pandoc\"\n\n    - packages:\n        - pandoc\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"{{ my_name }} - Install packages\"\n      package:\n        name: \"{{ packages }}\"\n        state: latest\n\n- ansible.builtin.import_playbook: retronas_system_config.yml\n  vars:\n    module_name: \"pandoc\"\n    module_state: \"present\"\n"
  },
  {
    "path": "ansible/install_pfsshell.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"pfsshell\"\n    my_file: \"install_pfsshell\"\n    module_name: \"pfsshell\"\n\n    packages:\n      - make\n      - gcc\n      - coreutils\n      - meson\n      - ninja-build\n      - libfuse-dev\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - name: \"{{ my_name }} - install script\"\n      ansible.builtin.template:\n        src: \"templates/{{ my_file }}/{{ my_file }}.sh.j2\"\n        dest: \"{{ retronas_root }}/scripts/{{ my_file }}.sh\"\n        owner: root\n        group: root\n        mode: '0750'\n\n    - name: \"{{ my_name }} - build source\"\n      ansible.builtin.command:\n        cmd: \"{{ retronas_root }}/scripts/{{ my_file }}.sh\"\n        creates: \"/usr/local/bin/pfsfuse\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_pi1541.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"pi1541\"\n    my_file: \"install_{{ my_name }}\"\n    module_name: \"pi1541\"\n\n    paths:\n      - { name: \"{{ my_name }}\", dest: \"{{ retronas_path }}\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n      - { name: \"{{ my_name }}\", dest: \"/mnt\" }\n\n    templates:\n      - { name: \"{{ my_name }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\"}\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_piscsi.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: true\n\n  vars:\n    my_name: \"piscsi\"\n    my_file: \"install_piscsi\"\n    module_name: \"piscsi\"\n\n    packages:\n      - bridge-utils\n      - build-essential\n      - disktype\n      - clang\n      - genisoimage\n      - git\n      - libev-dev\n      - libevdev2\n      - libgmock-dev\n      - libpcap-dev\n      - libpcap0.8-dev\n      - libprotobuf-dev\n      - libspdlog-dev\n      - man2html\n      - nginx-light\n      - protobuf-compiler\n      - python3\n      - python3-dev\n      - python3-pip\n      - python3-setuptools\n      - python3-venv\n      - python3-wheel\n      - unar\n      - unzip\n      - ca-certificates\n      - dosfstools\n      - kpartx\n      - unzip\n      - unar\n      - gettext\n      - rsyslog\n\n    paths:\n      - { name: \"{{ my_name }}\", dest: \"{{ retronas_path }}\", owner: \"{{ retronas_user }}\", group: '{{ retronas_group }}' }\n      - { name: \"src\", dest: \"{{ retronas_root }}\", owner: \"{{ retronas_user }}\", group: '{{ retronas_group }}' }\n      - { name: \"{{ my_name }}\", src: \"{{ retronas_path }}/{{ my_name }}\", dest: \"/home/{{ retronas_user }}\", state: \"link\" }\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\"}\n      - { name: \"{{ my_file }}_standard.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\"}\n      - { name: \"{{ my_name }}_retronas_patch.diff\", dest: \"{{ retronas_root }}/src\"}\n      - { name: \"{{ my_name }}.service\", dest: \"/etc/systemd/system\"}\n\n    systemd_units:\n      - { name: \"rsyslog\", type: 'service', state: \"restarted\", enabled: \"yes\", restart: \"yes\", instance: \"no\" }\n      - { name: \"piscsi\", type: 'service', state: \"started\", enabled: \"yes\", restart: \"yes\", instance: \"no\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      include_vars: retronas_vars.yml\n\n    # libpcap can conflict with libpcap from backport (tcpdump etc)\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - build source\"\n      ansible.builtin.command:\n        cmd: \"{{ retronas_root }}/scripts/install_{{ my_name }}.sh\"\n        creates: \"{{ retronas_root }}/bin/{{ my_name }}/{{ my_name }}\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - password file\"\n      ansible.builtin.copy:\n        dest: /etc/{{ my_name }}_passwd\n        content: retronas\n        mode: 0600\n        owner: root\n        group: root\n\n    - name: \"{{ my_name }} - enable service(s)\"\n      ansible.builtin.service:\n        name: \"{{ item.name }}.{{ item.type }}\"\n        state: \"{{ item.state }}\"\n        enabled: \"{{ item.enabled }}\"\n        daemon_reload: true\n      with_items: \"{{ systemd_units }}\"\n      when:\n        - item.instance == 'no'\n      notify: \"{{ my_name }} - Restart service\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item.name }}.{{ item.type }}\"\n        state: restarted\n        daemon_reload: true\n      with_items: \"{{ systemd_units }}\"\n      when:\n        - item.restart == 'yes'\n        - item.instance == 'no'\n"
  },
  {
    "path": "ansible/install_proftpd.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"ProFTPd\"\n    my_file: \"install_proftpd\"\n    module_name: \"proftpd\"\n\n    packages:\n      - avahi-daemon\n      - proftpd-core\n\n    templates:\n      - { name: \"retronas.conf\", dest: \"/etc/proftpd/conf.d\" }\n      - { name: \"ftp.service\", dest: \"/etc/avahi/services\" }\n\n    firewalld_rules:\n      - { name: ftp, zone: retro }\n      - { name: ftp, zone: modern }\n\n  tasks:\n\n    - name: \"{{ my_name }}  - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - remove  mod_unique_id.c\"\n      ansible.builtin.ini_file:\n        path: /etc/proftpd/modules.conf\n        section: null\n        option: \"LoadModule mod_unique_id.c\"\n        state: absent\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - enable startup services\"\n      ansible.builtin.service:\n        name: proftpd\n        state: started\n        enabled: true\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: restarted\n      with_items:\n        - avahi-daemon\n        - proftpd\n"
  },
  {
    "path": "ansible/install_ps2_openps2loader.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: true\n\n  vars:\n    my_name: \"PS2 OpenPS2Loader\"\n    my_file: \"install_ps2_openps2loader\"\n    my_dir: \"{{ retronas_path }}/ps2/OpenPS2Loader\"\n    module_name: \"ps2_openps2loader\"\n\n    paths:\n      - { name: \"APPS\",   dest: \"{{ my_dir }}\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n      - { name: \"ART\",    dest: \"{{ my_dir }}\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n      - { name: \"CFG\",    dest: \"{{ my_dir }}\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n      - { name: \"CHT\",    dest: \"{{ my_dir }}\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n      - { name: \"LNG\",    dest: \"{{ my_dir }}\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n      - { name: \"THM\",    dest: \"{{ my_dir }}\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n      - { name: \"VMC\",    dest: \"{{ my_dir }}\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n\n    templates:\n      - { name: \"retronas_ps2.conf\", dest: \"/etc/samba\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Include systems map\"\n      ansible.builtin.include_vars: \"retronas_systems.yml\"\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - name: \"{{ my_name }} - check old pops dir\"\n      ansible.builtin.stat:\n        path: \"{{ my_dir }}/POPS\"\n      register: old_pops\n\n    - debug:\n        msg: \"{{ old_pops }}\"\n\n    - name: \"{{ my_name }} - rename old pops dir\"\n      ansible.builtin.shell:\n        cmd: \"mv {{ my_dir }}/POPS {{ my_dir }}/POPS-OLD\"\n      when: old_pops.stat.exists is true\n\n    - name: \"{{ my_name }} - build symlinks\"\n      ansible.builtin.file:\n        src: \"../../roms/{{ item.src }}\"\n        dest: \"{{ my_dir }}/{{ item.ops2l }}\"\n        state: link\n      loop: \"{{ system_map }}\"\n      when:\n        - item.ops2l | length > 0\n\n    - name: \"{{ my_name }} - configure includes file\"\n      ansible.builtin.ini_file:\n        path: /etc/samba/smb.conf\n        section: ps2\n        option: \"include\"\n        value: \"/etc/samba/retronas_ps2.conf\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba\n"
  },
  {
    "path": "ansible/install_ps2_udpbd.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: true\n\n  vars:\n    my_name: \"ps2_udpbd\"\n    my_file: \"install_ps2_udpbd\"\n    module_name: \"ps2_udpbd\"\n    append_user_group: \"disk\"\n\n    systemd_units:\n      - { name: \"ps2_udpbd\", type: 'service', state: \"stopped\", enabled: \"no\", restart: \"no\", instance: \"no\" }\n\n    packages:\n      - make\n      - gcc\n      - g++\n      - git\n      - build-essential\n\n    paths:\n      - { name: \"{{ my_name }}\", dest: \"{{ retronas_path }}\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n\n    templates:\n      - { name: \"udpbd_manager.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"{{ my_name }}.service\", dest: \"/etc/systemd/system\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Include systems map\"\n      ansible.builtin.include_vars: \"retronas_systems.yml\"\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - Install from source code\"\n      ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n      args:\n        creates: \"{{ retronas_root}} /bin/ps2_udpbd\"\n\n    - name: \"{{ my_name }} - enable service(s)\"\n      ansible.builtin.service:\n        name: \"{{ item.name }}.{{ item.type }}\"\n        state: \"{{ item.state }}\"\n        enabled: \"{{ item.enabled }}\"\n        daemon_reload: true\n      with_items: \"{{ systemd_units }}\"\n      when:\n        - item.instance == 'no'\n      notify: \"{{ my_name }} - Restart service\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.update-user\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item.name }}.{{ item.type }}\"\n        state: restarted\n        daemon_reload: true\n      with_items: \"{{ systemd_units }}\"\n      when:\n        - item.restart == 'yes'\n        - item.instance == 'no'\n"
  },
  {
    "path": "ansible/install_ps3netsrv.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: true\n\n  vars:\n    my_name: \"ps3netsrv\"\n    my_file: \"install_ps3netsrv\"\n    module_name: \"ps3netsrv\"\n    system_key: \"ps3netsrv\"\n\n    systemd_units:\n      - { name: \"ps3netsrv\", type: 'service', state: \"started\", enabled: \"yes\", restart: \"yes\", instance: \"no\" }\n      - { name: \"ps3netsrv-perms\", type: 'service', state: \"started\", enabled: \"no\", restart: \"yes\", instance: \"no\" }\n      - { name: \"ps3netsrv-perms\", type: 'timer', state: \"started\", enabled: \"yes\", restart: \"yes\", instance: \"no\" }\n\n    packages:\n      - make\n      - automake\n      - autoconf\n      - gcc\n      - g++\n      - meson\n      - ninja-build\n      - curl\n      - wget\n      - build-essential\n      - unzip\n      - libmbedtls-dev\n      - coreutils\n      - jq\n\n    packages_debian:\n      debian10:\n        - libmbedtls12\n      debian11:\n        - libmbedtls12\n      debian12:\n        - libmbedtls14\n      debian13:\n        - libmbedtls21\n\n    packages_ubuntu:\n      - libmbedtls14\n\n    templates:\n      - { name: \"{{ my_file }}.sh\",         dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"ps3netsrv.service\",        dest: \"/usr/lib/systemd/system\" }\n      - { name: \"ps3netsrv-perms.service\",  dest: \"/usr/lib/systemd/system\" }\n      - { name: \"ps3netsrv-perms.timer\",    dest: \"/usr/lib/systemd/system\" }\n\n    firewalld_rules:\n      - { zones: retro, service: ps3netsrv }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Include systems map\"\n      ansible.builtin.include_vars: \"retronas_systems.yml\"\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - name: \"{{ my_name }} - Install build tools (debian)\"\n      ansible.builtin.package:\n        name: \"{{ packages_debian['debian' + ansible_distribution_major_version] }}\"\n        state: latest\n      when: ansible_distribution == 'Debian'\n\n    - name: \"{{ my_name }} - Install build tools (ubuntu)\"\n      ansible.builtin.package:\n        name: \"{{ packages_ubuntu }}\"\n        state: latest\n      when: ansible_distribution == 'Ubuntu'\n\n    - name: \"{{ my_name }} - build top level\"\n      ansible.builtin.file:\n        path: \"{{ retronas_path }}/ps3/ps3netsrv\"\n        owner: \"{{ retronas_user }}\"\n        group: \"{{ retronas_group }}\"\n        state: directory\n        mode: \"0775\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - build layout\"\n      ansible.builtin.file:\n        src: \"../../roms/{{ item.src }}\"\n        dest: \"{{ retronas_path }}/ps3/ps3netsrv/{{ item.ps3netsrv }}\"\n        owner: \"{{ retronas_user }}\"\n        group: \"{{ retronas_group }}\"\n        state: link\n      loop: \"{{ system_map }}\"\n      when:\n        - item.ps3netsrv | length > 0\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - Install from source code\"\n      ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n      args:\n        creates: \"{{ retronas_root}}/bin/ps3netsrv\"\n\n    - name: \"{{ my_name }} - enable service(s)\"\n      ansible.builtin.service:\n        name: \"{{ item.name }}.{{ item.type }}\"\n        state: \"{{ item.state }}\"\n        enabled: \"{{ item.enabled }}\"\n        daemon_reload: true\n      with_items: \"{{ systemd_units }}\"\n      when:\n        - item.instance == 'no'\n      notify: \"{{ my_name }} - Restart service\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item.name }}.{{ item.type }}\"\n        state: restarted\n        daemon_reload: true\n      with_items: \"{{ systemd_units }}\"\n      when:\n        - item.restart == 'yes'\n        - item.instance == 'no'\n\n    - name: \"{{ my_name }} - Restart instances\"\n      ansible.builtin.service:\n        name: \"{{ item.1.name }}{{ item.0.dest }}.{{ item.1.type }}\"\n        state: restarted\n        daemon_reload: true\n      with_items:\n        - \"{{ system_map }}\"\n        - \"{{ systemd_units }}\"\n      when:\n        - item.1.restart == 'yes'\n        - item.1.instance == 'yes'\n"
  },
  {
    "path": "ansible/install_pygopherd.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"pygopherd\"\n    my_file: \"install_pygopherd\"\n    module_name: \"pygopherd\"\n\n    packages:\n      - python3\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"{{ my_name }}.conf\", dest: \"{{ retronas_root }}/etc\" }\n      - { name: \"{{ my_name }}.service\", dest: \"/usr/lib/systemd/system\" }\n\n    firewalld_ports:\n      - { port: 70, protocol: tcp }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - build source\"\n      ansible.builtin.command:\n        cmd: \"{{ retronas_root }}/scripts/{{ my_file }}.sh\"\n        creates: \"opt/pygopherd/bin/pygopherd\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: restarted\n        daemon_reload: true\n      with_items:\n        - \"{{ my_name }}\"\n"
  },
  {
    "path": "ansible/install_rclone.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"rclone\"\n    my_file: \"install_rclone\"\n    module_name: \"rclone\"\n\n    packages:\n      - rclone\n\n    templates:\n      - { name: \"{{ my_name }}-webui.service\", dest: \"/etc/systemd/system\", mode: \"0644\" }\n\n    services:\n      - \"{{ my_name }}-webui.service\"\n\n    firewalld_ports:\n      - { port: 5572, protocol: tcp, zone: retro }\n      - { port: 5572, protocol: tcp, zone: modern }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n      notify: \"{{ my_name }} - Restart service\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n      notify: \"{{ my_name }} - Restart service\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.htpasswd\n      notify: \"{{ my_name }} - Restart service\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        enabled: true\n        state: restarted\n        daemon_reload: true\n      loop: \"{{ services }}\"\n"
  },
  {
    "path": "ansible/install_recalbox_cifs.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"Recalbox CIFS\"\n    my_file: \"install_recalbox_cifs\"\n    module_name: \"recalbox_cifs\"\n    system_key: \"recalbox\"\n\n    top_level_paths:\n      - { name: \"ROMS\",  enabled: yes,  generic: \"roms\",  systems: yes }\n      - { name: \"SAVES\", enabled: yes,  generic: \"saves\", systems: yes  }\n      - { name: \"BIOS\",  enabled: yes,  generic: \"bios\",  systems: yes  }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.extradirs\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba.system\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_redumper.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"redumper\"\n    my_file: \"install_{{ my_name }}\"\n    module_name: \"redumper\"\n\n    packages:\n      - unzip\n      - jq\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\"}\n\n  tasks:\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - Install\"\n      ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n      args:\n        creates: \"{{ retronas_root}}/bin/ps3netsrv\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_retroaimserver.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: true\n\n  vars:\n    my_name: \"retroaimserver\"\n    my_file: \"install_retroaimserver\"\n    my_dir: \"/opt/retro-aim-server\"\n    module_name: \"retroaimserver\"\n\n    systemd_units:\n      - { name: \"retro-aim-server\", type: 'service', state: \"started\", enabled: \"yes\", restart: \"yes\", instance: \"no\" }\n\n    packages:\n      - curl\n\n    templates:\n      - { name: \"{{ my_file }}.sh\",         dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"retro-aim-server.service\", dest: \"/etc/systemd/system/\" }\n\n    firewalld_rules:\n      - { zones: retro, service: retroaimserver }\n\n    settings_env:\n      - { regex: \"^export OSCAR_HOST=.*\", line: \"export OSCAR_HOST={{ retronas_net_retro_ip }}\" }\n      - { regex: \"^export DISABLE_AUTH=true\", line: \"export DISABLE_AUTH=false\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - Install from source code\"\n      ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n      args:\n        creates: \"{{ my_dir }}/retro_aim_server\"\n\n    - name: \"{{ my_name }} - enable service(s)\"\n      ansible.builtin.service:\n        name: \"{{ item.name }}.{{ item.type }}\"\n        state: \"{{ item.state }}\"\n        enabled: \"{{ item.enabled }}\"\n        daemon_reload: true\n      with_items: \"{{ systemd_units }}\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - configure defaults\"\n      ansible.builtin.lineinfile:\n        path: \"{{ my_dir }}/settings.env\"\n        regexp: \"{{ item.regex }}\"\n        line: \"{{ item.line }}\"\n      with_items: \"{{ settings_env }}\"\n\n    #\n    # FIREWALL\n    #\n    - name: \"{{ my_name }} - checking firewall rule\"\n      ansible.builtin.stat:\n        path: /etc/firewalld/services\n      register: firewalld_services\n\n    - name: \"{{ my_name }} - Generate firewall service\"\n      ansible.builtin.template:\n        src: \"templates/{{ my_file }}/retroaimserver.xml.j2\"\n        dest: \"/etc/firewalld/services/retroaimserver.xml\"\n        owner: root\n        group: root\n        mode: 0644\n      when: firewalld_services.stat.exists\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item.name }}.{{ item.type }}\"\n        state: restarted\n        daemon_reload: true\n      with_items: \"{{ systemd_units }}\"\n"
  },
  {
    "path": "ansible/install_retroarch_cifs.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"RetroArch Based Systems CIFS\"\n    my_file: \"install_retroarch_cifs\"\n    module_name: \"retroarch_cifs\"\n    system_key: \"retroarch\"\n\n  tasks:\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba.system\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_retrodeck_cifs.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"retrodeck CIFS\"\n    my_file: \"install_retrodeck_cifs\"\n    module_name: \"retrodeck_cifs\"\n    system_key: \"retrodeck\"\n\n    top_level_paths:\n      - { name: \"roms\",   enabled: yes,  generic: \"roms\",   systems: yes }\n    #  - { name: \"saves\",  enabled: yes,  generic: \"saves\",  systems: yes }\n      - { name: \"bios\",   enabled: yes,  generic: \"bios\",   systems: yes }\n\n    internal_symlinks:\n      - { src: 'commodore/amiga', dest: 'ags' }\n      - { src: 'commodore/amiga', dest: 'amiga600' }\n      - { src: 'capcom/cps1', dest: 'cps' }\n      - { src: 'mame/mame', dest: 'mame-advmame' }\n      - { src: 'nec/pcenginecd', dest: 'tg-cd' }\n      - { src: 'sega/megacd', dest: 'segacd' }\n      - { src: 'sega/megacd', dest: 'megacdjp' }\n      - { src: 'sega/megadrive', dest: 'genesis' }\n      - { src: 'sega/megadrive', dest: 'megadrivejp' }\n      - { src: 'snk/neogeocd', dest: 'neogeocdjp' }\n      - { src: 'sega/saturn', dest: 'saturnjp' }\n      - { src: 'sega/32x', dest: 'sega32xjp' }\n      - { src: 'sega/32x', dest: 'sega32xna' }\n      - { src: 'nintendo/superfamicom', dest: 'sneshd' }\n      - { src: 'nintendo/superfamicom', dest: 'snesna' }\n\n  tasks:\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.extradirs\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba.system\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_romdir.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    module_name: \"romdir\"\n\n  tasks:\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_romimport.yml",
    "content": "---\n- hosts: localhost\n\n  vars:\n    my_name: \"ROM Import\"\n    my_file: \"install_romimport\"\n    module_name: \"romimport\"\n\n    packages:\n      - python3\n      - git\n\n    paths:\n      - { name: \"romimport\", dest: \"{{ retronas_path }}\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n\n    templates:\n      - { name: \"romimport.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\"}\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"{{ my_name }} - Load RetroNAS Systems\"\n      ansible.builtin.include_vars: retronas_systems.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - Install from source code\"\n      ansible.builtin.shell: \"git clone https://github.com/frederic-mahe/Hardware-Target-Game-Database.git\"\n      args:\n        chdir: \"{{ retronas_root}}/bin\"\n        creates: \"{{ retronas_root}}/bin/Hardware-Target-Game-Database\"\n\n    - name: \"{{ my_name }} - set SMBD permissions\"\n      ansible.builtin.shell: \"chown -R {{ retronas_user}}:{{ retronas_user }} Hardware-Target-Game-Database\"\n      args:\n        chdir: \"{{ retronas_root}}/bin\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_romm_cifs.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"RomM CIFS\"\n    my_file: \"install_romm_cifs\"\n    module_name: \"romm_cifs\"\n    system_key: \"romm\"\n    script_url: 'https://raw.githubusercontent.com/minorOffense/romdirflattener/refs/heads/main/flatten-romdir.sh'\n    script_dest: \"{{ retronas_root }}/bin/flatten-romdir.sh\"\n    fattenerscript: \"retronas-romm-dirs\"\n\n    top_level_paths:\n      - { name: \"roms\",   enabled: yes,  generic: \"roms\",   systems: yes }\n\n    templates:\n      - { name: \"retronas-romm-dirs.service\", dest: \"/etc/systemd/system\" }\n      - { name: \"retronas-romm-dirs.timer\", dest: \"/etc/systemd/system\" }\n      - { name: \"retronas-romm-dirs.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n\n    systemd_units:\n      - { name: \"retronas-romm-dirs\", type: 'service', state: \"stopped\", enabled: \"no\", restart: \"no\", instance: \"no\" }\n      - { name: \"retronas-romm-dirs\", type: 'timer',   state: \"started\", enabled: \"yes\", restart: \"no\", instance: \"no\" }\n\n    # needs review or be handled in the flatten script  \n    #internal_symlinks:\n    #  - { src: 'sega/advancedpicobeena', dest: 'beena' }\n    #  - { src: 'panasonic/3do', dest: 'panasonic-m2' }\n    #  - { src: 'apple/pippin', dest: 'pippin' }\n    #  - { src: 'interton/vc4000', dest: 'vc-4000' }\n    #  - { src: 'tandy/vis', dest: 'vis' }\n    #  - { src: 'tapwave/zodiac', dest: 'zod' }\n    #    # 'amiga-cd',         # ??\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"{{ my_name }} - get update script\"\n      ansible.builtin.shell: curl -kLso \"{{ script_dest }}\" {{ script_url }}\n\n    - name: \"{{ my_name }} - make script executable\"\n      file:\n        path: \"{{ script_dest }}\"\n        mode: '0755'\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba.system\n      vars:\n        nolink: true\n        writable: \"no\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - create startup service(s) instance\"\n      ansible.builtin.template:\n        src: templates/{{ my_file }}/{{ item.name }}.{{ item.type }}.j2\n        dest: /etc/systemd/system/{{ item.name }}.{{ item.type }}\n        owner: root\n        group: root\n        mode: 0644\n      with_items: \"{{ systemd_units }}\"\n      notify: \"{{ my_name }} daemon-reload\"\n\n    - name: \"Import system-config role\"\n      ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n    - name: Run flatten-romdir.sh for each src/dest (no top_level)\n      ansible.builtin.shell: \"{{ retronas_root }}/bin/flatten-romdir.sh {{ retronas_path }}/roms/{{ item.src }} {{ retronas_path }}/{{ system_key }}/roms/{{ item[system_key] }}\"\n      become: true\n      loop: \"{{ system_map }}\"\n      when: top_level_paths is defined and\n            item[system_key] is defined and\n            item[system_key] | length > 0\n\n    - name: patch up perms\n      ansible.builtin.shell: \"chown -R {{ retronas_user }}:{{ retronas_group }} {{ retronas_path }}/{{ system_key }}\"\n      become: true\n\n  handlers:\n\n    - name: \"{{ my_name }} daemon-reload\"\n      ansible.builtin.systemd:\n        daemon_reload: true\n"
  },
  {
    "path": "ansible/install_sabretools.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: true\n\n  vars:\n    my_name: \"SabreTools\"\n    my_file: \"install_sabretools\"\n    module_name: \"sabretools\"\n\n    packages:\n      - git\n      - coreutils\n      - unzip\n\n    my_arch: \"x86_64\"\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\"}\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - Run installer\"\n      ansible.builtin.shell:\n        cmd: \"./{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n        chdir: \"{{ retronas_root }}/scripts\"\n        executable: /bin/bash\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_samba.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  roles:\n    - retronas.role.samba\n\n  vars:\n    module_name: \"samba\"\n\n  tasks:\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_seaweedfs.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"seaweedfs\"\n    my_file: \"install_{{ my_name }}\"\n    module_name: \"seaweedfs\"\n\n    systemd_units:\n      - { name: \"{{ my_name }}-retronas\", type: 'service', state: \"started\", enabled: \"yes\", restart: \"yes\", instance: \"no\" }\n\n    packages:\n      - pwgen\n\n    firewalld_rules:\n      - { zones: retro, service: \"{{ my_name }}\" }\n      - { zones: modern, service: \"{{ my_name }}\" }\n\n    templates:\n      - { name: \"{{ my_name }}-retronas.service\", dest: \"/etc/systemd/system\" }\n      - { name: \"install_{{ my_name }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"seaweedfs-credentials.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"weed-retronas-s3.json\", dest: \"{{ retronas_root }}/bin\", mode: \"0640\", force: no }\n\n    paths:\n      - { name: \"s3\", dest: \"{{ retronas_path }}\", state: \"directory\", mode: \"0755\"}\n\n  tasks:\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - name: \"generate access key\"\n      shell:\n        cmd: pwgen -s 32 1\n      no_log: true\n      register: retronas_s3_access_key\n\n    - name: \"generate secret key\"\n      shell:\n        cmd: pwgen -s 64 1\n      no_log: true\n      register: retronas_s3_secret_key\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - Install release\"\n      ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n      args:\n        creates: \"{{ retronas_root}}/bin/weed\"\n\n    - name: \"{{ my_name }} - enable service(s)\"\n      ansible.builtin.service:\n        name: \"{{ item.name }}.{{ item.type }}\"\n        state: \"{{ item.state }}\"\n        enabled: \"{{ item.enabled }}\"\n        daemon_reload: true\n      with_items: \"{{ systemd_units }}\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    #\n    # FIREWALL\n    #\n    - name: \"{{ my_name }} - checking firewall rule\"\n      ansible.builtin.stat:\n        path: /etc/firewalld/services\n      register: firewalld_services\n\n    - name: \"{{ my_name }} - templates\"\n      ansible.builtin.template:\n        src: \"templates/{{ my_file }}/{{ my_name }}.xml.j2\"\n        dest: /etc/firewalld/services/{{ my_name }}.xml\n        owner: root\n        group: root\n        mode: 0644\n        force: true\n      when: firewalld_services.stat.exists\n\n    - ansible.builtin.shell:\n        cmd: 'sleep 10'\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item.name }}.{{ item.type }}\"\n        state: restarted\n        daemon_reload: true\n      with_items: \"{{ systemd_units }}\"\n"
  },
  {
    "path": "ansible/install_sit.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"sit\"\n    my_file: \"install_sit\"\n    module_name: \"sit\"\n\n    packages:\n      - make\n      - gcc\n      - git\n      - coreutils\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - build source\"\n      ansible.builtin.command:\n        cmd: \"{{ retronas_root }}/scripts/{{ my_file }}.sh\"\n        creates: \"/usr/local/bin/sit\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_smbmounter.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"smbmounter\"\n    my_file: \"install_{{ my_name }}\"\n    module_name: \"smbmounter\"\n    system_key: \"smbmounter\"\n\n    templates:\n      - { name: \"retronas_{{ my_name }}.conf\", dest: \"/etc/samba\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Include systems map\"\n      ansible.builtin.include_vars: \"retronas_systems.yml\"\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.extradirs\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba\n\n    - name: \"{{ my_name }} - link romdir\"\n      ansible.builtin.file:\n        src: \"{{ retronas_path }}/roms/commodore/amiga\"\n        dest: \"{{ retronas_path }}/amiga\"\n        owner: \"{{ retronas_user }}\"\n        group: \"{{ retronas_group }}\"\n        state: link\n        mode: \"0775\"\n\n    - name: \"{{ my_name }} - configure includes file\"\n      ansible.builtin.ini_file:\n        path: /etc/samba/smb.conf\n        section: \"amiga\"\n        option: \"include\"\n        value: \"/etc/samba/retronas_smbmounter.conf\"\n      notify: \"restart samba\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n      notify: \"restart samba\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_sslcert.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  roles:\n    - retronas.role.sslcert\n\n  vars:\n    module_name: \"sslcert\"\n\n  tasks:\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_syncthing.yml",
    "content": "---\n- hosts: localhost\n\n  vars:\n    my_name: \"Syncthing\"\n    my_file: \"install_syncthing\"\n    module_name: \"syncthing\"\n\n    packages:\n      - syncthing\n\n    firewalld_rules:\n      - { zone: modern, service: syncthing }\n      - { zone: modern, service: syncthing-gui }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"{{ my_name }} - Package signing key\"\n      ansible.builtin.get_url:\n        url: https://syncthing.net/release-key.gpg\n        dest: /etc/apt/trusted.gpg.d/syncthing-archive-keyring.gpg\n        owner: root\n        group: root\n        mode: \"0644\"\n\n    - name: \"{{ my_name }} - Configure APT repo\"\n      ansible.builtin.apt_repository:\n        repo: deb https://apt.syncthing.net/ syncthing stable\n        state: present\n        filename: syncthing\n        update_cache: true\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - name: \"{{ my_name }} - Web config http://{{ ansible_default_ipv4.address }}:8384\"\n      ansible.builtin.ini_file:\n        path: /usr/lib/systemd/system/syncthing@.service\n        section: Service\n        option: ExecStart\n        value: \"/usr/bin/syncthing serve --no-browser --no-restart --logflags=0 --gui-address=0.0.0.0:8384\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - enable startup services\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: started\n        enabled: true\n        daemon_reload: true\n      with_items:\n        - syncthing@{{ retronas_user }}\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: restarted\n        daemon_reload: true\n      with_items:\n        - syncthing@{{ retronas_user }}\n"
  },
  {
    "path": "ansible/install_tcpser.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"tcpser\"\n    my_file: \"install_tcpser\"\n    module_name: \"tcpser\"\n    append_user_group: \"dialout\"\n\n    packages:\n      - make\n      - gcc\n      - coreutils\n      - git\n\n    paths:\n      - { name: \"{{ my_name }}\", dest: \"{{ retronas_root }}/etc\" }\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"tcpser@.service\", dest: \"/usr/lib/systemd/system\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - ansible.builtin.import_role:\n        name: retronas.role.update-user\n\n    - name: \"{{ my_name }} - build source\"\n      ansible.builtin.command:\n        cmd: \"{{ retronas_root }}/scripts/{{ my_file }}.sh\"\n        creates: \"{{ retronas_root }}/bin/tcpser\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_telnet.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: true\n\n  vars:\n    my_name: \"Telnet\"\n    my_file: \"install_telnet\"\n    module_name: \"telnet\"\n\n    templates:\n      - { name: \"telnet\", dest: \"/etc/xinetd.d\", force: false }\n\n    firewalld_rules:\n      - { zone: \"retro\", service: \"telnet\" }\n\n    packages:\n      - telnet\n      - telnetd\n      - xinetd\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - name: \"{{ my_name }} - Enable startup services\"\n      ansible.builtin.service:\n        name: xinetd\n        state: started\n        enabled: true\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart services\"\n      ansible.builtin.service:\n        name: xinetd\n        state: restarted\n"
  },
  {
    "path": "ansible/install_tftpd-hpa.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"tftpd-hpa\"\n    my_file: \"install_tftpd-hpa\"\n    module_name: \"tftpd-hpa\"\n\n    firewalld_rules:\n      - { zone: \"retro\", service: \"tftp\" }\n\n    packages:\n      tftpd-hpa\n\n    templates:\n      - { name: \"tftpd-hpa\", dest: \"/etc/default\" }\n\n  tasks:\n    - name: \"{{ my_name }}  - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - enable startup services\"\n      ansible.builtin.service:\n        name: tftpd-hpa\n        state: started\n        enabled: true\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: tftpd-hpa\n        state: restarted\n"
  },
  {
    "path": "ansible/install_tnfs.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"TNFS Atari 8-bit and ZX Spectrum\"\n    my_file: \"install_tnfs\"\n    module_name: \"tnfs\"\n\n    packages:\n      - build-essential\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"tnfsd.service\", dest: \"/usr/lib/systemd/system\" }\n\n\n    firewalld_ports:\n      - { zone: \"retro\", port: 16384, protocol: tcp }\n      - { zone: \"retro\", port: 16384, protocol: udp }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - Install from source code\"\n      ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n      # args:\n      #   creates: \"{{ retronas_root}}/bin/tnfs\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - enable startup service\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: started\n        enabled: true\n        daemon_reload: true\n      with_items:\n        - tnfsd\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: restarted\n        daemon_reload: true\n      with_items:\n        - tnfsd\n"
  },
  {
    "path": "ansible/install_troubleshooting.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"troubleshooting\"\n    my_file: \"install_troubleshooting\"\n    module_name: \"troubleshooting\"\n\n    packages:\n      - ioping\n      - iotop\n      - iperf3\n      - tcpdump\n      - iftop\n      - traceroute\n      - ethtool\n      - socat\n      - nmap\n      - bind9-dnsutils\n      - mtr\n      - strace\n      - curl\n      - smartmontools\n      - htop\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_ucon64.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"ucon64\"\n    my_file: \"install_ucon64\"\n    module_name: \"ucon64\"\n\n    packages:\n      - make\n      - gcc\n      - coreutils\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - build source\"\n      ansible.builtin.command:\n        cmd: \"{{ retronas_root }}/scripts/{{ my_file }}.sh\"\n        creates: \"/usr/local/bin/ucon64\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_waybackproxy.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"waybackproxy\"\n    my_file: \"install_{{ my_name }}\"\n    my_dir: \"/opt/{{ my_name }}\"\n    module_name: \"waybackproxy\"\n\n    systemd_units:\n      - { name: \"{{ my_name }}\", type: 'service', state: \"started\", enabled: \"no\", restart: \"yes\", instance: \"no\" }\n\n    templates:\n      - { name: \"config.json\", dest: \"{{ my_dir }}\" }\n      - { name: \"{{ my_name }}.service\", dest: \"/etc/systemd/system\" }\n      - { name: \"install_waybackproxy.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n\n    packages:\n      - git\n\n    paths:\n      - { name: \"{{ my_name }}\", dest: \"/opt\", state: \"directory\", mode: \"0755\" }\n\n    firewalld_ports:\n      - { zone: \"retro\", port: 8888, protocol: tcp }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - name: \"{{ my_name }} - Install\"\n      shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n      args:\n        creates: \"/opt/{{ my_name }}/{{ my_name }}.py\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      service:\n        name: \"{{ my_name }}.service\"\n        state: restarted\n        daemon_reload: true\n"
  },
  {
    "path": "ansible/install_webone.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: true\n\n  vars:\n    my_name: \"WebOne proxy\"\n    my_file: \"install_webone\"\n    module_name: \"webone\"\n\n    packages:\n      - git\n      - ffmpeg\n      - coreutils\n\n    packages_debian:\n      debian12:\n        - imagemagick-6.q16\n        - imagemagick-6-common\n      debian13:\n        - imagemagick-7.q16\n        - imagemagick-7-common\n\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"webone.service\", dest: \"/usr/lib/systemd/system\" }\n\n    firewalld_ports:\n      - { zone: \"retro\", port: 8080, protocol: tcp }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n      notify: \"{{ my_name }} - Restart service\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n      vars:\n        packages: \"{{ packages_debian['debian' + ansible_distribution_major_version ] }}\"\n      when: ansible_distribution == 'Debian'\n      notify: \"{{ my_name }} - Restart service\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.dotnetcore8\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - Run installer\"\n      ansible.builtin.shell:\n        cmd: \"./{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n        chdir: \"{{ retronas_root }}/scripts\"\n        executable: /bin/bash\n        creates: \"{{ retronas_root }}/bin/webone/webone\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: webone\n        state: restarted\n        daemon_reload: true\n"
  },
  {
    "path": "ansible/install_wrp.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"wrp\"\n    my_file: \"install_{{ my_name }}\"\n    module_name: \"wrp\"\n\n    packages:\n      - chromium\n\n    google_chrome: /usr/bin/google-chrome\n    chromium: /usr/bin/chromium\n\n    templates:\n      - { name: \"{{ my_file }}.sh\",       dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"{{ my_name }}.service\",  dest: \"/usr/lib/systemd/system\" }\n\n    firewalld_ports:\n      - { zone: \"retro\", port: 8080, protocol: tcp }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"{{ my_name }} - check architecture\"\n      ansible.builtin.shell:\n        cmd: dpkg --print-architecture | head -n1\n      register: architecture\n\n    - name: \"{{ my_name }} - check google-chrome\"\n      ansible.builtin.stat:\n        path: \"{{ google_chrome }}\"\n      register: google_chrome_check\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n      when: google_chrome_check.stat.exists is false\n\n    - name: \"{{ my_name }} - fake google-chrome\"\n      ansible.builtin.file:\n        src: \"{{ chromium }}\"\n        dest: \"{{ google_chrome }}\"\n        state: link\n      when: google_chrome_check.stat.exists is false\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - Run installer\"\n      ansible.builtin.shell:\n        cmd: \"./{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n        chdir: \"{{ retronas_root }}/scripts\"\n        executable: /bin/bash\n\n    - name: \"{{ my_name }} - enable startup service\"\n      ansible.builtin.service:\n        name: \"{{ my_name }}\"\n        state: started\n        enabled: true\n        daemon_reload: true\n\n    - ansible.builtin.import_role:\n        name: retronas.role.firewalld.port\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ my_name }}\"\n        state: restarted\n        daemon_reload: true\n"
  },
  {
    "path": "ansible/install_xbox.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"xbox\"\n    my_file: \"install_xbox\"\n    module_name: \"xbox\"\n    system_key: \"xbox\"\n\n    paths:\n      - { name: \"xbox\", dest: \"{{ retronas_path }}\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n\n    templates:\n      - { name: \"retronas_xbox.conf\", dest: \"/etc/samba/\"  }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - symlinks\"\n      ansible.builtin.file:\n        src: \"{{ item.src }}\"\n        dest: \"{{ retronas_path }}/xbox/{{ item.dest }}\"\n        owner: \"{{ retronas_user }}\"\n        group: \"{{ retronas_group }}\"\n        state: link\n      with_items:\n        - { src: \"../roms/microsoft/xbox/games\", dest: \"games\" }\n        - { src: \"../roms/microsoft/xbox/iso\", dest: \"iso\" }\n\n    - name: \"{{ my_name }} - configure includes file\"\n      ansible.builtin.ini_file:\n        path: /etc/samba/smb.conf\n        section: xbox\n        option: \"include\"\n        value: \"/etc/samba/retronas_xbox.conf\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_xbox360.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"XBox360\"\n    my_file: \"install_xbox360\"\n    module_name: \"xbox360\"\n    system_key: \"xbox360\"\n\n    paths:\n      - { name: \"xbox360\", dest: \"{{ retronas_path }}\" }\n\n    templates:\n      - { name: \"retronas_xbox360.conf\", dest: \"/etc/samba\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.samba\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - symlinks\"\n      ansible.builtin.file:\n        src: \"{{ item.src }}\"\n        dest: \"{{ retronas_path }}/xbox360/{{ item.dest }}\"\n        owner: \"{{ retronas_user }}\"\n        group: \"{{ retronas_group }}\"\n        state: link\n      with_items:\n        - { src: \"../roms/microsoft/xbox360/games\", dest: \"games\" }\n\n    - name: \"{{ my_name }} - configure includes file\"\n      ansible.builtin.ini_file:\n        path: /etc/samba/smb.conf\n        section: xbox360\n        option: \"include\"\n        value: \"/etc/samba/retronas_xbox360.conf\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_xbox360_netiso.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  vars:\n    my_name: \"XBox360 NetISO\"\n    my_file: \"install_xbox360_netiso\"\n    module_name: \"xbox360_netiso\"\n    system_key: \"xbox360\"\n\n    packages:\n      - unzip\n      - curl\n      - jq\n\n    paths:\n      - { name: \"xbox360\", dest: \"{{ retronas_path }}\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n\n    templates:\n      - { name: \"install_xbox360_netiso.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"xbox360_netiso.service\", dest: \"/etc/systemd/system\" }\n      - { name: \"dummy.iso\", dest: \"{{ retronas_path }}/{{ system_key }}\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.romdir\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - name: \"{{ my_name }} - symlinks\"\n      ansible.builtin.file:\n        src: \"{{ item.src }}\"\n        dest: \"{{ retronas_path }}/xbox360/{{ item.dest }}\"\n        owner: \"{{ retronas_user }}\"\n        group: \"{{ retronas_group }}\"\n        state: link\n      with_items:\n        - { src: \"../roms/microsoft/xbox360/games\", dest: \"games\" }\n\n    - name: \"{{ my_name }} - Install from git repo\"\n      ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"xbox360_netiso.service\"\n        state: restarted\n        enabled: true\n        daemon_reload: true\n"
  },
  {
    "path": "ansible/install_xboxmanager.yml",
    "content": "---\n# Dependencies\n- ansible.builtin.import_playbook: install_extract-xiso.yml\n\n- hosts: localhost\n  gather_facts: true\n\n  roles:\n    - retronas.role.cockpit\n    - retronas.role.filesystems\n    - retronas.role.curlftpfs\n\n  vars:\n    my_name: \"xboxmanager\"\n    my_file: \"install_xboxmanager\"\n    my_path: \"/opt\"\n    module_name: \"xboxmanager\"\n\n    paths:\n      - { name: \"device-mounts\",  dest: \"{{ retronas_path }}\",                owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n      - { name: \"xbox\",           dest: \"{{ retronas_path }}/device-mounts\",  owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n\n    templates:\n      - { name: \"xboxmanager.cfg\", dest: \"/opt/xboxmanager\", owner: \"{{ retronas_user }}\", group: \"{{ retronas_group }}\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"{{ my_name }} - clone repo\"\n      ansible.builtin.git:\n        repo: 'https://github.com/sairuk/cockpit-xboxmanager.git'\n        dest: \"{{ my_path }}/xboxmanager\"\n        clone: true\n        update: true\n        version: \"main\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.paths\n\n    - name: \"{{ my_name }} - run installer\"\n      ansible.builtin.command:\n        cmd: \"{{ my_path }}/xboxmanager/xboxmanager-install.sh\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n"
  },
  {
    "path": "ansible/install_xlink-kai.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n  become: true\n\n  # https://dist.teamxlink.co.uk/\n\n  vars:\n    my_name: \"xlink-kai\"\n    my_file: \"install_xlink-kai\"\n    my_keyrings: \"/etc/apt/keyrings\"\n    my_key: \"{{ my_keyrings }}/teamxlink.gpg\"\n    my_repo: \"https://dist.teamxlink.co.uk/linux/debian\"\n    module_name: \"xlink-kai\"\n\n    prerequisite_packages:\n      - ca-certificates\n      - gnupg\n      - curl\n\n    packages:\n      - xlinkkai\n\n    templates:\n      - { name: \"{{ my_name }}.service\", dest: \"/etc/systemd/system\" }\n\n    firewalld_ports:\n      - { zone: \"modern\", port: 30000, protocol: udp }\n      - { zone: \"retro\", port: 34522, protocol: tcp }\n      - { zone: \"modern\", port: 34522, protocol: tcp }\n\n\n  tasks:\n\n  - name: \"{{ my_name }} - Load RetroNAS config\"\n    ansible.builtin.include_vars: retronas_vars.yml\n\n  - name: \"{{ my_name }} - clean up old key\"\n    ansible.builtin.shell: \"rm -f /usr/share/keyrings/teamxlink.asc\"\n\n  - name: \"{{ my_name }} - Install prerequisite packages\"\n    ansible.builtin.package:\n      name: \"{{ prerequisite_packages }}\"\n      update_cache: true\n\n  - name: \"{{ my_name }} - Create key dir\"\n    ansible.builtin.file:\n      path: \"{{ my_keyrings }}\"\n      mode: 0755\n      owner: root\n      group: root\n      state: directory\n\n  - name: \"{{ my_name }} - Add teamxlink key\"\n    ansible.builtin.shell:\n      cmd: \"curl -fsSL {{ my_repo }}/gpg | gpg --dearmor -o {{ my_key }}\"\n      creates: \"{{ my_key }}\"\n\n  - name: \"{{ my_name }} - Fix perms on file\"\n    ansible.builtin.file:\n      path: \"{{ my_key }}\"\n      mode: 0755\n      owner: root\n      group: root\n\n  - name: \"{{ my_name }} - Add teamxlink repository\"\n    ansible.builtin.copy:\n      dest: \"/etc/apt/sources.list.d/teamxlink.list\"\n      content: |\n        deb [signed-by={{ my_key }}] {{ my_repo }}/static/deb/release/ /\n\n  - ansible.builtin.import_role:\n      name: retronas.role.package.latest\n\n  - name: \"{{ my_name }} - check for arm build\"\n    ansible.builtin.stat:\n      path: \"/usr/bin/kaiengine_arm\"\n    register: arm_build\n\n  - name: \"{{ my_name }} - link arm build\"\n    ansible.builtin.file:\n      src: \"/usr/bin/kaiengine_arm\"\n      dest: \"/usr/bin/kaiengine\"\n      state: link\n    when: arm_build.stat.exists is true\n\n  - ansible.builtin.import_role:\n      name: retronas.role.templates\n    notify: \"{{ my_name }} - Restart service(s)\"\n\n  - name: \"{{ my_name }} - fix config perms for {{ retronas_user }}\"\n    ansible.builtin.file:\n      path: \"/etc/kaiengine.conf\"\n      owner: \"{{ retronas_user }}\"\n      group: \"{{ retronas_user }}\"\n      mode: '0640'\n    notify: \"{{ my_name }} - Restart service(s)\"\n\n  - ansible.builtin.import_role:\n      name: retronas.role.firewalld.port\n\n  - ansible.builtin.import_role:\n      name: retronas.role.system-config\n\n  handlers:\n    - name: \"{{ my_name }} - Restart service(s)\"\n      ansible.builtin.service:\n        name: \"{{ my_name }}.service\"\n        state: started\n        enabled: true\n        daemon_reload: true\n"
  },
  {
    "path": "ansible/install_ytree.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: no\n\n  vars:\n    my_name: \"ytree\"\n    my_file: \"install_{{ my_name }}\"\n    module_name: \"ytree\"\n\n    packages:\n      - build-essential\n      - libreadline-dev\n      - gcc\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n\n  tasks:\n\n  - name: \"{{ my_name }} - Load RetroNAS config\"\n    ansible.builtin.include_vars: retronas_vars.yml\n\n  - name: \"{{ my_name }} - Load RetroNAS systems\"\n    ansible.builtin.include_vars: retronas_systems.yml\n\n  - ansible.builtin.import_role:\n      name: retronas.role.package.latest\n\n  - ansible.builtin.import_role:\n      name: retronas.role.templates\n\n  - name: \"{{ my_name }} - Install from source code\"\n    ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n\n  - ansible.builtin.import_role:\n      name: retronas.role.system-config"
  },
  {
    "path": "ansible/install_zterm.yml",
    "content": "---\n- hosts: localhost\n\n  vars:\n    my_name: \"zterm\"\n    my_file: \"install_{{ my_name }}\"\n    module_name: \"zterm\"\n    append_user_group: dialout\n\n    systemd_units:\n      - { name: \"zterm\", type: 'service', state: \"started\", enabled: \"yes\", restart: \"yes\", instance: \"no\" }\n\n    packages:\n      - make\n      - cmake\n      - git\n      - build-essential\n      - lrzsz\n\n    templates:\n      - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\" }\n      - { name: \"zterm.service\", dest: \"/usr/lib/systemd/system\" }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Include systems map\"\n      ansible.builtin.include_vars: \"retronas_systems.yml\"\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - ansible.builtin.import_role:\n        name: retronas.role.package.latest\n\n    - ansible.builtin.import_role:\n        name: retronas.role.templates\n      notify: \"{{ my_name }} - Restart service\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.update-user\n\n    - name: \"{{ my_name }} - Install from source code\"\n      ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n      args:\n        creates: \"{{ retronas_root}} /opt/zterm/zterm\"\n\n    - name: \"{{ my_name }} - enable service(s)\"\n      ansible.builtin.service:\n        name: \"{{ item.name }}.{{ item.type }}\"\n        state: \"{{ item.state }}\"\n        enabled: \"{{ item.enabled }}\"\n        daemon_reload: true\n      with_items:\n        \"{{ systemd_units }}\"\n      when:\n        - item.instance == 'no'\n      notify: \"{{ my_name }} - Restart service\"\n\n    - ansible.builtin.import_role:\n        name: retronas.role.system-config\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item.name }}.{{ item.type }}\"\n        state: restarted\n        daemon_reload: true\n      with_items:\n        \"{{ systemd_units }}\"\n      when:\n        - item.restart == 'yes'\n        - item.instance == 'no'\n\n    - name: \"{{ my_name }} - Restart instances\"\n      ansible.builtin.service:\n        name: \"{{ item.1.name }}{{ item.0.dest }}.{{ item.1.type }}\"\n        state: restarted\n        daemon_reload: true\n      with_items:\n        - \"{{ system_map }}\"\n        - \"{{ systemd_units }}\"\n      when:\n        - item.1.restart == 'yes'\n        - item.1.instance == 'yes'\n"
  },
  {
    "path": "ansible/migrate_mister_cifs_issue21.yml",
    "content": "---\n### migration playbook for issue #21\n# this issue is old and the fix is slow so users who haven't updated in a while\n# will have to run this manually with\n#\n# ansible-playbook migrate_mister_cifs_issue21.yml\n#\n\n# Dependencies\n- ansible.builtin.import_playbook: install_samba.yml\n- ansible.builtin.import_playbook: install_romdir.yml\n- ansible.builtin.import_playbook: install_extradirs.yml\n\n- hosts: localhost\n\n  vars:\n    - my_name: \"MiSTer CIFS\"\n    - my_file: \"install_mister_cifs\"\n\n    - top_level_paths:\n        - { name: \"games\",      enabled: yes,  generic: \"roms\",        systems: yes }\n        - { name: \"saves\",      enabled: yes,  generic: \"saves\",       systems: yes }\n        - { name: \"savestates\", enabled: yes,  generic: \"savestates\",  systems: yes }\n        - { name: \"BIOS\",       enabled: yes,  generic: \"bios\",        systems: yes }\n        - { name: \"wallpapers\", enabled: yes,  generic: \"wallpapers\",  systems: no  }\n\n  tasks:\n\n    - name: \"{{ my_name }} - Include systems map\"\n      ansible.builtin.include_vars: \"retronas_systems.yml\"\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"{{ my_name }} - build top level\"\n      ansible.builtin.file:\n        path: \"{{ retronas_path }}/mister\"\n        owner: \"{{ retronas_user }}\"\n        group: \"{{ retronas_group }}\"\n        state: directory\n        mode: \"0775\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"get facts for path\"\n      ansible.builtin.stat:\n        path: \"{{ retronas_path }}/mister/{{ item.mister }}\"\n      with_items:\n        - \"{{ system_map }}\"\n      register: mister_stat\n\n    - name: \"{{ my_name }} - removing old symlink layout see issue #21\"\n      ansible.builtin.file:\n        path: \"{{ retronas_path }}/mister/{{ item.item.mister }}\"\n        state: absent\n      with_items:\n        - \"{{ mister_stat.results }}\"\n      when: item.stat.islnk is defined and item.stat.islnk == True\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - build top level share paths\"\n      ansible.builtin.file:\n        path: \"{{ retronas_path }}/mister/{{ item.name }}\"\n        owner: \"{{ retronas_user }}\"\n        group: \"{{ retronas_group }}\"\n        state: directory\n        mode: \"0775\"\n      with_items:\n        \"{{ top_level_paths }}\"\n      when:\n        - item.enabled is true\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - build systems directory layout\"\n      ansible.builtin.file:\n        src: \"../../{{ item.1.generic|lower }}/{{ item.0.src }}\"\n        dest: \"{{ retronas_path }}/mister/{{ item.1.name }}/{{ item.0.mister }}\"\n        owner: \"{{ retronas_user }}\"\n        group: \"{{ retronas_group }}\"\n        state: link\n      with_nested:\n        - \"{{ system_map }}\"\n        - \"{{ top_level_paths }}\"\n      when:\n        - item.1.enabled is true\n        - item.0.mister | length > 0\n        - item.1.systems is true\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - configure includes file\"\n      ansible.builtin.ini_file:\n        path: /etc/samba/smb.conf\n        section: mister\n        option: \"include\"\n        value: \"/etc/samba/retronas_mister.conf\"\n      notify: \"{{ my_name }} - Restart service\"\n\n    - name: \"{{ my_name }} - configure retro shares\"\n      ansible.builtin.template:\n        src: \"templates/{{ my_file }}/retronas_mister.conf.j2\"\n        dest: /etc/samba/retronas_mister.conf\n        owner: root\n        group: root\n        mode: '0644'\n      notify: \"{{ my_name }} - Restart service\"\n\n  handlers:\n\n    - name: \"{{ my_name }} - Restart service\"\n      ansible.builtin.service:\n        name: \"{{ item }}\"\n        state: restarted\n      with_items:\n        - smbd\n        - nmbd\n\n- ansible.builtin.import_playbook: retronas_system_config.yml\n  vars:\n    module_name: \"mister_cifs\"\n    module_state: \"present\"\n"
  },
  {
    "path": "ansible/retronas_create_dirs.yml",
    "content": "---\n- name: \"{{ my_name }} - Include systems map\"\n  ansible.builtin.include_vars: \"retronas_systems.yml\"\n\n- name: \"{{ my_name }} - Load RetroNAS config\"\n  ansible.builtin.include_vars: retronas_vars.yml\n\n- name: \"{{ my_name }} - stat our path\"\n  ansible.builtin.stat:\n    path: \"{{ retronas_path }}\"\n  register: rn_path\n\n- name: \"{{ my_name }} - create our base\"\n  ansible.builtin.shell:\n    cmd: \"mkdir -p {{ rn_path}}\"\n  changed_when: false\n  when: rn_path is defined and\n        rn_path.stat.exists is false\n\n- name: \"{{ my_name }} - perms\"\n  ansible.builtin.shell:\n    cmd: \"chown -R {{ retronas_user }}:{{ retronas_group }} {{ rn_path }}\"\n  changed_when: false\n  when: rn_path is defined and\n        rn_path.stat.exists is false\n\n- name: \"{{ my_name }} - stat top level paths\"\n  ansible.builtin.stat:\n    path: \"{{ retronas_path }}/{{ item }}\"\n  loop: [ \"roms\", \"bios\", \"saves\", \"savestates\" ]\n  register: tl_paths\n\n- name: \"{{ my_name }} - create our missing paths\"\n  ansible.builtin.file:\n    path: \"{{ retronas_path }}/{{ item.item }}\"\n    owner: \"{{ retronas_user }}\"\n    group: \"{{ retronas_group }}\"\n    state: directory\n    mode: '0755'\n  loop: \"{{ tl_paths.results }}\"\n  when: tl_paths is defined and\n        item.stat.exists is false\n\n- name: \"{{ my_name }} - build layout list (set fact)\"\n  ansible.builtin.set_fact:\n    path_list: \"{{ path_list|default([]) + [ retronas_path + '/' + item.1.name + '/' + item.0.src if item.1.systems is true else '' ] }}\"\n  no_log: true\n  loop: \"{{ system_map|product(top_level_paths)|list }}\"\n  when: item.1.enabled is true\n\n- name: \"{{ my_name }} - build layout\"\n  ansible.builtin.shell:\n    cmd: mkdir -p {{ path_list|flatten|join(' ') }}\n  become: true\n  become_user: \"{{ retronas_user }}\"\n  when: path_list is defined\n  changed_when: False\n\n- name: \"{{ my_name }} - build systems directory layout (set fact)\"\n  ansible.builtin.set_fact:\n    link_list: \"{{ link_list|default([]) + [ 'ln -sfT ' + retronas_path  + '/' + item.1.name|lower + '/' + item.0.src + ' ' + retronas_path  + '/' +  item.1.name + '/' + item.0.dest + ';' ] }}\"\n  loop: \"{{ system_links|product(top_level_paths)|list }}\"\n  when:\n    - item.1.enabled is true\n    - item.1.systems is true\n\n- name: \"{{ my_name }} - build systems directory layout (link list)\"\n  ansible.builtin.shell:\n    cmd: \"{{ link_list|join('') }}\"\n  when: link_list is defined\n  changed_when: false\n\n- name: \"{{ my_name }} - add top level directory info\"\n  ansible.builtin.copy:\n    dest: \"{{ item.stat.path }}/dir.txt\"\n    content: |\n      this folder structure is the responsiblity of the user to populate\n    owner: \"{{ retronas_user }}\"\n    group: \"{{ retronas_group }}\"\n    mode: \"0644\"\n  loop: \"{{ tl_paths.results }}\"\n  when: tl_paths is defined and\n        item.stat.exists is true\n"
  },
  {
    "path": "ansible/retronas_dependencies.yml",
    "content": "---\n- hosts: localhost\n  become: yes\n\n  vars:\n    my_name: \"RetroNAS dependencies\"\n    my_file: \"retronas_dependencies\"\n\n    packages:\n      - jq\n      - sudo\n      - git\n      - dialog\n\n    directories:\n      - bin\n      - etc\n      - scripts\n      - log\n      - cache\n\n  tasks:\n\n    - name: \"{{ my_name }} - Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"{{ my_name }} - configure program directories\"\n      ansible.builtin.file:\n        path: \"{{ retronas_root }}/{{ item }}\"\n        owner: root\n        group: root\n        mode: 0755\n        state: directory\n      with_items: \"{{ directories }}\"\n\n    - name: \"{{ my_name }} - configure top level share directory\"\n      ansible.builtin.file:\n        path: \"{{ retronas_path }}\"\n        owner: \"{{ retronas_user }}\"\n        group: \"{{ retronas_user }}\"\n        mode: 0775\n        state: directory\n\n    - ansible.builtin.include_role:\n        name: \"{{ role }}\"\n      loop:\n        - retronas.role.package.latest\n        - retronas.role.cache\n      loop_control:\n        loop_var: role\n"
  },
  {
    "path": "ansible/retronas_system_config.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: true\n\n  roles:\n    - retronas.role.system-config\n"
  },
  {
    "path": "ansible/retronas_systems.yml",
    "content": "---\n#                            __________________________\n#                       .-:=[ RetroNAS_Systems_Mapping ]=:-.\n#\n# Please keep everything organized, make sure if adding a new system all keys\n# are populated and the entry is in the appropriate area and order.\n#\n# usage\n# - use the retronas-systems-manager.py script in the maint directory to manage\n#   new systems and projects (keys) in this file\n# - keep systems in their subgroups based on parent rom directory\n# - combine subgroups into other groups using union (see: system_map)\n#\n# terms:\n# - system: generally a manufacturer but the term system is carry over from og\n#   retronas setup with this file\n# - project: batocera is a project and will be added to all entries as a blank\n#   key you can populate later\n#\n# adding a new system\n# - retronas-systems-manager.py --add-new-system generic\n#\n# checking for project before adding\n# - retronas-systems-manager.py --check-project bestsoftware\n#\n# adding a new project\n# - retronas-systems-manager.py --add-new-project bestsoftware\n#\n\nsystem_template:\n  - src: ''\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n\nsystem_links:\n  - src: nec/pcengine\n    dest: nec/turbografx16\n  - src: nec/pcenginecd\n    dest: nec/turbografxcd\n  - src: nintendo/famicom\n    dest: nintendo/nes\n  - src: nintendo/superfamicom\n    dest: nintendo/snes\n  - src: sega/megacd\n    dest: sega/segacd\n  - src: sega/megadrive\n    dest: sega/genesis\n\nsystem_map:\n  - src: acorn/archimedes\n    last: ''\n    mister: ARCHIE\n    retropie: ''\n    batocera: archimedes\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: acorn-archimedes\n    replayfpgaarcade: ''\n    retrodeck: archimedes\n    romm: acorn-archimedes\n    pretty_name: Acorn Achimedes\n  - src: acorn/atom\n    last: ''\n    mister: AcornAtom\n    retropie: ''\n    batocera: atom\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: atom\n    pretty_name: Acorn Atom\n  - src: acorn/bbcmicro\n    last: ''\n    mister: BBCMicro\n    retropie: ''\n    batocera: bbcmicro\n    recalbox: bbcmicro\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: bbcmicro\n    igdb: bbcmicro\n    replayfpgaarcade: ''\n    retrodeck: bbcmicro\n    romm: bbcmicro\n    pretty_name: Acorn BBC Micro\n  - src: acorn/electron\n    last: ''\n    mister: AcornElectron\n    retropie: ''\n    batocera: electron\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: acorn-electron\n    replayfpgaarcade: ''\n    retrodeck: electron\n    romm: acorn-electron\n    pretty_name: Acorn Electron\n  - src: amstrad/cpc\n    last: ''\n    mister: Amstrad\n    retropie: ''\n    batocera: amstradcpc\n    recalbox: amstradcpc\n    retroarch: Amstrad - CPC\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: amstradcpc\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: amstradcpc\n    igdb: acpc\n    replayfpgaarcade: ''\n    retrodeck: amstradcpc\n    romm: acpc\n    pretty_name: Amstrad CPC\n  - src: amstrad/pcw\n    last: ''\n    mister: Amstrad PCW\n    retropie: ''\n    batocera: pcw\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: apcw\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: amstrad-pcw\n    pretty_name: Amstrad PCW\n  - src: amstrad/gx4000\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: gx4000\n    recalbox: gx4000\n    retroarch: Amstrad - GX4000\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: amstradgx4000\n    smdb: ''\n    freestation: gx4000\n    analoguepocket: ''\n    emudeck: gx4000\n    igdb: gx4000\n    replayfpgaarcade: ''\n    retrodeck: gx4000\n    romm: 'amstrad-gx4000'\n    pretty_name: Amstrad GX4000\n  - src: antonic/galaksija\n    last: other/antonic_galaksija\n    mister: Galaksija\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Antonic Galaksija\n  - src: apf/mp1000\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: apfm1000\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: apf\n    pretty_name: APF MP1000\n  - src: apogee/bk-01\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: bk-01\n    pretty_name: Apogee BK-01\n  - src: apple/applei\n    last: ''\n    mister: APPLE-I\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: 'apple'\n    pretty_name: Apple 1\n  - src: apple/appleii\n    last: ''\n    mister: Apple-II\n    retropie: ''\n    batocera: apple2\n    recalbox: apple2\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: apple2\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: apple2\n    igdb: appleii\n    replayfpgaarcade: ''\n    retrodeck: apple2\n    romm: appleii\n    pretty_name: Apple II\n  - src: apple/appleii\n    last: ''\n    mister: TK2000\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: Apple - II\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: TK2000\n  - src: apple/appleiigs\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: apple2gs\n    recalbox: apple2gs\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: apple2gs\n    igdb: apple-iigs\n    replayfpgaarcade: ''\n    retrodeck: apple2gs\n    romm: apple-iigs\n    pretty_name: Apple II/GS\n  - src: apple/appleiii\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: 'appleiii'\n    pretty_name: Apple III\n  - src: apple/lisa\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: apple-lisa\n    pretty_name: Apple Lisa\n  - src: apple/macintosh\n    last: ''\n    mister: MACPLUS\n    retropie: ''\n    batocera: macintosh\n    recalbox: macintosh\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: macintosh\n    igdb: mac\n    replayfpgaarcade: ''\n    retrodeck: macintosh\n    romm: mac\n    pretty_name: Apple Machintosh\n  - src: apple/newton\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: newton\n    pretty_name: Apple Newton\n  - src: apple/pippin\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: apple-pippin\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: apple-pippin\n    pretty_name: Apple Pippin\n  - src: arcade\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: arcade\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: arcade\n    igdb: arcade\n    replayfpgaarcade: ''\n    retrodeck: arcade\n    romm: arcade\n    pretty_name: Arcade\n  - src: dice\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: dice\n    recalbox: dice\n    retroarch: DICE\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: arcade\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: mame/hbmame\n    last: ''\n    mister: hbmame\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: HBMAME\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: HBMAME\n  - src: mame/mame\n    last: ''\n    mister: mame\n    retropie: ''\n    batocera: mame\n    recalbox: mame\n    retroarch: MAME\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: mame\n    smdb: ''\n    freestation: mame\n    analoguepocket: ''\n    emudeck: mame\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: mame\n    romm: ''\n    pretty_name: MAME\n  - src: mame/mess\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: mess\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: mess\n    romm: ''\n    pretty_name: MESS\n  - src: mame/mame2000\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: MAME 2000\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: mame/mame2003p\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: MAME 2003-Plus\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: mame/mame2003\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: MAME 2003\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: mame/mame2010\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: MAME 2010\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: mame/mame2015\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: MAME 2015\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: mame/mame2016\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: MAME 2016\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: mame/namco22\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: namco22\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: finalburn/fba\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: fba\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: fba\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: fba\n    romm: ''\n    pretty_name: Final Burn Alpha\n  - src: finalburn/fbneo\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: fbneo\n    recalbox: fbneo\n    retroarch: FBNeo - Arcade Games\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: fbn\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: fbneo\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: fbneo\n    romm: ''\n    pretty_name: Final Burn NEO\n  - src: gaelco\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: gaelco\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Gaelco\n  - src: snk/mvs\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: neogeomvs\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: neogeomvs\n    pretty_name: SNK MVS\n  - src: snk/hyper-neo-geo-64\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: hyper-neo-geo-64\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: hyper-neo-geo-64\n    pretty_name: SNK Hyper NEO-GEO 64\n  - src: laserdisc\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: daphne\n    recalbox: daphne\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: daphne\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: daphne\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: daphne\n    romm: ''\n    pretty_name: Daphne\n  - src: laserdisc\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: singe\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: laserdisc\n    romm: ''\n    pretty_name: LaserDisc\n  - src: pgm2\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: pgm2\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: PGM2\n  - src: 'astral/2000'\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: astral-2000\n    pretty_name: Astral 2000\n  - src: atari/2600\n    last: ''\n    mister: Atari2600\n    retropie: ''\n    batocera: atari2600\n    recalbox: atari2600\n    retroarch: Atari - 2600\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: atari2600\n    smdb: Atari 2600 SMDB.txt\n    freestation: a2600\n    analoguepocket: '2600'\n    emudeck: atari2600\n    igdb: atari2600\n    replayfpgaarcade: ''\n    retrodeck: atari2600\n    romm: atari2600\n    pretty_name: Atari 2600\n  - src: atari/5200\n    last: ''\n    mister: ATARI5200\n    retropie: ''\n    batocera: atari5200\n    recalbox: atari5200\n    retroarch: Atari - 5200\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: atari5200\n    smdb: Atari 5200 SMDB.txt\n    freestation: a5200\n    analoguepocket: ''\n    emudeck: atari5200\n    igdb: atari5200\n    replayfpgaarcade: ''\n    retrodeck: atari5200\n    romm: atari5200\n    pretty_name: Atari 5200\n  - src: atari/7800\n    last: ''\n    mister: ATARI7800\n    retropie: ''\n    batocera: atari7800\n    recalbox: atari7800\n    retroarch: Atari - 7800\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: atari7800\n    smdb: Atari 7800 SMDB.txt\n    freestation: a7800\n    analoguepocket: '7800'\n    emudeck: atari7800\n    igdb: atari7800\n    replayfpgaarcade: ''\n    retrodeck: atari7800\n    romm: atari7800\n    pretty_name: Atari 7800\n  - src: atari/800\n    last: ''\n    mister: ATARI800\n    retropie: ''\n    batocera: atari800\n    recalbox: atari800\n    retroarch: Atari - 8-bit Family\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: atari800\n    smdb: ''\n    freestation: a800\n    analoguepocket: ''\n    emudeck: atari800\n    igdb: atari8bit\n    replayfpgaarcade: ''\n    retrodeck: atari800\n    romm: atari800\n    pretty_name: Atari 8-bit\n  - src: atari/jaguar\n    last: ''\n    mister: Jaguar\n    retropie: ''\n    batocera: jaguar\n    recalbox: jaguar\n    retroarch: Atari - Jaguar\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: atarijaguar\n    smdb: Atari Jaguar SMDB.txt\n    freestation: ''\n    analoguepocket: ''\n    emudeck: atarijaguar\n    igdb: jaguar\n    replayfpgaarcade: ''\n    retrodeck: atarijaguar\n    romm: jaguar\n    pretty_name: Atari Jaguar\n  - src: atari/jaguar/cd\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: jaguarcd\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: atari-jaguar-cd\n    replayfpgaarcade: ''\n    retrodeck: atarijaguarcd\n    romm: atari-jaguar-cd\n    pretty_name: Atari Jaguar CD\n  - src: atari/lynx\n    last: ''\n    mister: AtariLynx\n    retropie: ''\n    batocera: lynx\n    recalbox: lynx\n    retroarch: Atari - Lynx\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: atarilynx\n    smdb: Atari Lynx SMDB.txt\n    freestation: lynx\n    analoguepocket: lynx\n    emudeck: atarilynx\n    igdb: lynx\n    replayfpgaarcade: ''\n    retrodeck: atarilynx\n    romm: lynx\n    pretty_name: Atari Lynx\n  - src: atari/st\n    last: ''\n    mister: AtariST\n    retropie: ''\n    batocera: atarist\n    recalbox: atarist\n    retroarch: Atari - ST\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: atarist\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: atarist\n    igdb: atari-st\n    replayfpgaarcade: ''\n    retrodeck: atarist\n    romm: atari-st\n    pretty_name: Atari ST\n  - src: atari/st/flop\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Atari ST (flop)\n  - src: atari/st/cart\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Atari ST (cart)\n  - src: atari/vcs\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: atari-vcs\n    pretty_name: Atari VCS\n  - src: atari/xegs\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: xegs\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: atarixe\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: atarixe\n    romm: atari-xegs\n    pretty_name: Atari XE\n  - src: atmel/uzebox\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: uzebox\n    recalbox: uzebox\n    retroarch: Uzebox\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: uzebox\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: uzebox\n    igdb: uzebox\n    replayfpgaarcade: ''\n    retrodeck: uzebox\n    romm: uzebox\n    pretty_name: Atmel Uzebox\n  - src: audiosonic\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Audiosonic\n  - src: bally/astrocade\n    last: ''\n    mister: Astrocade\n    retropie: ''\n    batocera: astrocade\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: astrocade\n    analoguepocket: ''\n    emudeck: astrocde\n    igdb: astrocade\n    replayfpgaarcade: ''\n    retrodeck: astrocde\n    romm: astrocade\n    pretty_name: Bally Astrocade\n  - src: bandai/rx78\n    last: ''\n    mister: RX78\n    retropie: ''\n    batocera: rx78\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Bandai RX78\n  - src: bandai/sumfami\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: sufami\n    recalbox: sufami\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: sufami\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: sufami-turbo\n    pretty_name: Bandai Sumfami\n  - src: bandai/wonderswan\n    last: ''\n    mister: WonderSwan\n    retropie: ''\n    batocera: wswan\n    recalbox: wswan\n    retroarch: Bandai - WonderSwan\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: wonderswan\n    smdb: Wonderswan SMDB.txt\n    freestation: ''\n    analoguepocket: wonderswan\n    emudeck: wonderswan\n    igdb: wonderswan\n    replayfpgaarcade: ''\n    retrodeck: wonderswan\n    romm: wonderswan\n    pretty_name: Bandai WonderSwan\n  - src: bandai/wonderswancolor\n    last: ''\n    mister: WonderSwanColor\n    retropie: ''\n    batocera: wswanc\n    recalbox: wswanc\n    retroarch: Bandai - WonderSwan Color\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: wonderswancolor\n    smdb: Wonderswan SMDB.txt\n    freestation: ''\n    analoguepocket: ''\n    emudeck: wonderswancolor\n    igdb: wonderswan-color\n    replayfpgaarcade: ''\n    retrodeck: wonderswancolor\n    romm: wonderswan-color\n    pretty_name: Bandai WonderSwan Color\n  - src: bandai/playdia\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: playdia\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: playdia\n    pretty_name: Bandai Playdia\n  - src: bandai/terebikko\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: terebikko-slash-see-n-say-video-phone\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: terebikko-slash-see-n-say-video-phone\n    pretty_name: Bandai Terebikko\n  - src: bandai/swancrystal\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: swancrystal\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: swancrystal\n    pretty_name: Bandai WonderSwan Crystal\n  - src: bandai/tamagotchi\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: tamagotchi\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: tamagotchi_p1\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Bandai Tamagotchi\n  - src: bbc/bridgecompanion\n    last: ''\n    mister: BBCBridgeCompanion\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: BBC Bridge Companion\n  - src: benesse/pocketchallengev2\n    last: ''\n    mister: PocketChallengeV2\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: pocket-challenge-v2\n    pretty_name: Benesse Pocket Challenge V2\n  - src: benesse/pocketchallengew\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: pocket-challenge-w\n    pretty_name: Benesse Pocket Challenge W\n  - src: bitcorporation/gamate\n    last: ''\n    mister: Gamate\n    retropie: ''\n    batocera: gamate\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: gamate\n    emudeck: ''\n    igdb: gamate\n    replayfpgaarcade: ''\n    retrodeck: gamate\n    romm: gamate\n    pretty_name: Bit Corporation Gamate\n  - src: bitcorporation/bit60\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Bit Corporation Bit-60\n  - src: bitcorporation/bit70\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Bit Corporation Bit-70\n  - src: bitcorporation/bit90\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: bit-90\n    pretty_name: Bit Corporation Bit-90\n  - src: cambridge/edsac\n    last: other/cambridge_edsac\n    mister: EDSAC\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: edsac\n    pretty_name: Cambridge EDSAC\n  - src: cambridgecomputer/z88\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: z88\n    pretty_name: Cambridge Z88\n  - src: camputers/lynx\n    last: other/camputers_lynx\n    mister: Lynx48\n    retropie: ''\n    batocera: camplynx\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: camputers-lynx\n    pretty_name: Camputers Lynx\n  - src: casio/loopy\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: loopy\n    recalbox: ''\n    retroarch: Casio - Loopy\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: casio-loopy\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: casio-loopy\n    pretty_name: Casio Loopy\n  - src: casio/pv1000\n    last: ''\n    mister: Casio_PV-1000\n    retropie: ''\n    batocera: pv1000\n    recalbox: ''\n    retroarch: Casio - PV-1000\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: pv1000\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: pv1000\n    romm: casio-pv-1000\n    pretty_name: Casio PV1000\n  - src: casio/pv2000\n    last: ''\n    mister: Casio_PV-2000\n    retropie: ''\n    batocera: pv2000\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: casio-pv-2000\n    pretty_name: Casio PV2000\n  - src: casio/cfx-9850\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: casio-cfx-9850\n    pretty_name: Casio CFX-9850\n  - src: casio/fp-1000\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: casio-fp-1000\n    pretty_name: Casio FP-1000\n  - src: casio/pb-1000\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: casio-pb-1000\n    pretty_name: Casio PB-1000\n  - src: cdc/cyber70\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: cdccyber70\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: cdccyber70\n    pretty_name: CDC Cyber70\n  - src: coleco/colecovision\n    last: other/coleco_colecovision\n    mister: Coleco\n    retropie: ''\n    batocera: colecovision\n    recalbox: colecovision\n    retroarch: Coleco - ColecoVision\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: colecovision\n    smdb: Colecovision SMDB.txt\n    freestation: coleco\n    analoguepocket: coleco\n    emudeck: colecovision\n    igdb: colecovision\n    replayfpgaarcade: ''\n    retrodeck: colecovision\n    romm: colecovision\n    pretty_name: Coleco Colecovision\n  - src: coleco/adam\n    last: ''\n    mister: Adam\n    retropie: ''\n    batocera: adam\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: adam\n    romm: colecoadam\n    pretty_name: Coleco Adam\n  - src: coleco/telstararcade\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: telstar-arcade\n    pretty_name: Coleco Telstar Arcade\n  - src: commodore/amiga\n    last: ''\n    mister: Amiga\n    retropie: ''\n    batocera: amiga500\n    recalbox: amiga600\n    retroarch: Commodore - Amiga\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: amiga\n    smdb: ''\n    freestation: amiga\n    analoguepocket: amiga\n    emudeck: amiga\n    igdb: amiga\n    replayfpgaarcade: ''\n    retrodeck: amiga\n    romm: amiga\n    pretty_name: Commodore Amiga\n  - src: commodore/amiga\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ags\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ags\n    romm: ''\n    pretty_name: ''\n  - src: commodore/amiga/iso\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: commodore/amiga1200\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: amiga1200\n    recalbox: amiga1200\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: amiga1200\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: amiga1200\n    romm: ''\n    pretty_name: Commodore Amiga 1200\n  - src: commodore/amigacd32\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: amigacd32\n    recalbox: amigacd32\n    retroarch: Commodore - CD32\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: amigacd32\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: amigacd32\n    igdb: amiga-cd32\n    replayfpgaarcade: ''\n    retrodeck: amigacd32\n    romm: amiga-cd32\n    pretty_name: Commodore Amiga CD32\n  - src: commodore/amigacdtv\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: amigacdtv\n    recalbox: amigacdtv\n    retroarch: Commodore - CDTV\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: cdtv\n    igdb: commodore-cdtv\n    replayfpgaarcade: ''\n    retrodeck: cdtv\n    romm: commodore-cdtv\n    pretty_name: Commodore Amiga CDTV\n  - src: commodore/commodore16\n    last: ''\n    mister: C16\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: c16\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: c16\n    igdb: c16\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: c16\n    pretty_name: Commodore 16\n  - src: commodore/commodore64\n    last: ''\n    mister: C64\n    retropie: ''\n    batocera: c64\n    recalbox: c64\n    retroarch: Commodore - 64\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: c64\n    smdb: ''\n    freestation: c64\n    analoguepocket: c64\n    emudeck: c64\n    igdb: c64\n    replayfpgaarcade: c64\n    retrodeck: c64\n    romm: c64\n    pretty_name: Commodore 64\n  - src: commodore/commodore128\n    last: ''\n    mister: C128\n    retropie: ''\n    batocera: c128\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: c128\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: c128\n    pretty_name: Commodore 128\n  - src: commodore/pet\n    last: ''\n    mister: PET2001\n    retropie: ''\n    batocera: pet\n    recalbox: ''\n    retroarch: Commodore - PET\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: cpet\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: cpet\n    pretty_name: Commodore PET\n  - src: commodore/vic20\n    last: ''\n    mister: VIC20\n    retropie: ''\n    batocera: c20\n    recalbox: vic20\n    retroarch: Commodore - VIC-20\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: vic20\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: vic20\n    igdb: vic-20\n    replayfpgaarcade: vic20\n    retrodeck: vic20\n    romm: vic-20\n    pretty_name: Commodore VIC20\n  - src: commodore/commodoreplus4\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: cplus4\n    recalbox: ''\n    retroarch: Commodore - Plus-4\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: c-plus-4\n    replayfpgaarcade: ''\n    retrodeck: plus4\n    romm: c-plus-4\n    pretty_name: Commodore Plus 4\n  - src: compukit/uk101\n    last: other/compukit_uk101\n    mister: UK101\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: CompuKit UK101\n  - src: creatronic/megaduck\n    last: ''\n    mister: MegaDuck\n    retropie: ''\n    batocera: megaduck\n    recalbox: megaduck\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: megaduck\n    smdb: ''\n    freestation: ''\n    analoguepocket: mega_duck\n    emudeck: megaduck\n    igdb: mega-duck-slash-cougar-boy\n    replayfpgaarcade: ''\n    retrodeck: megaduck\n    romm: mega-duck-slash-cougar-boy\n    pretty_name: Creatronic Mega Duck\n  - src: dec/pdp1\n    last: ''\n    mister: PDP1\n    retropie: ''\n    batocera: pdp1\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: pdp1\n    emudeck: ''\n    igdb: pdp1\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: pdp1\n    pretty_name: DEC PDP-1\n  - src: dec/pdp7\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: pdp-7\n    pretty_name: DEC PDP-7\n  - src: dec/pdp8\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: pdp-8--1\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: pdp-8\n    pretty_name: DEC PDP-8\n  - src: dec/pdp10\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: pdp10\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: pdp10\n    pretty_name: DEC PDP-10\n  - src: dec/pdp11\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: pdp11\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: pdp11\n    pretty_name: DEC PDP-11\n  - src: dec/gt40\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: gt40\n    pretty_name: DEC GT40\n  - src: dragon\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: dragon64\n    recalbox: dragon\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: dragon32\n    igdb: dragon-32-slash-64\n    replayfpgaarcade: ''\n    retrodeck: dragon32\n    romm: dragon-32-slash-64\n    pretty_name: Dragon 32/64\n  - src: dragon\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: tanodragon\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: tanodragon\n    romm: ''\n    pretty_name: ''\n  - src: eaca/eg2000\n    last: ''\n    mister: eg2000\n    retropie: ''\n    batocera: cgenie\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: colour-genie\n    pretty_name: EACA EG2000 Colour Genie\n  - src: electronika/bk\n    last: other/electronika_bk\n    mister: BK0011M\n    retropie: ''\n    batocera: bk\n    recalbox: bk\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: bk\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: bk\n    pretty_name: Electronika Bk\n  - src: elektor/tvgc\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: Elektor - TV Games Computer\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: elektor-tv-games-computer\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: elektor\n    pretty_name: ''\n  - src: emerson/arcadia2001\n    last: other/emerson_arcadia2001\n    mister: Arcadia\n    retropie: ''\n    batocera: arcadia\n    recalbox: ''\n    retroarch: Emerson - Arcadia 2001\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: arcadia\n    emudeck: arcadia\n    igdb: arcadia-2001\n    replayfpgaarcade: ''\n    retrodeck: arcadia\n    romm: arcadia-2001\n    pretty_name: Eemerson Arcadia 2001\n  - src: entex/adventurevision\n    last: other/entex_adventurevision\n    mister: AVision\n    retropie: ''\n    batocera: advision\n    recalbox: ''\n    retroarch: Entex - Adventure Vision\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: advision\n    analoguepocket: avision\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: 'adventure-vision'\n    pretty_name: Entex AdventureVision\n  - src: epoch_co/scv\n    last: ''\n    mister: SCV\n    retropie: ''\n    batocera: scv\n    recalbox: scv\n    retroarch: Epoch - Super Cassette Vision\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: epoch-super-cassette-vision\n    replayfpgaarcade: ''\n    retrodeck: scv\n    romm: epoch-super-cassette-vision\n    pretty_name: Epoch Co Super Cassette Vision\n  - src: epoch_co/cv\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: cassettevision\n    recalbox: ''\n    retroarch: Epoch - Cassette Vision\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: epoch-cassette-vision\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: epoch-cassette-vision\n    pretty_name: Epoch Co Cassette Vision\n  - src: epoch_co/galaxy2\n    last: ''\n    mister: EpochGalaxyII\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Epoch Co Galaxy 2\n  - src: epoch_co/gamepocket\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: gamepock\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: epoch-game-pocket-computer\n    pretty_name: Epoch Co Game Pocket\n  - src: exidy/sorcerer\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: exidy-sorcerer\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: exidy-sorcerer\n    pretty_name: Exidy Sorcerer\n  - src: fairchild/channelf\n    last: other/fairchild_channelf\n    mister: ChannelF\n    retropie: ''\n    batocera: channelf\n    recalbox: channelf\n    retroarch: Fairchild - Channel F\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: channel_f\n    emudeck: channelf\n    igdb: fairchild-channel-f\n    replayfpgaarcade: ''\n    retrodeck: channelf\n    romm: fairchild-channel-f\n    pretty_name: Fairchild Channel F\n  - src: fantasyconsole/pico8\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: pico8\n    recalbox: pico8\n    retroarch: PICO-8\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: pico8\n    smdb: ''\n    freestation: pico8\n    analoguepocket: ''\n    emudeck: pico8\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: pico8\n    romm: ''\n    pretty_name: Lexaloffle PICO-8\n  - src: fantasyconsole/tic80\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: tic80\n    recalbox: tic80\n    retroarch: TIC-80\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: tic-80\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: tic80\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: tic80\n    romm: tic-80\n    pretty_name: TIC-80\n  - src: fantasyconsole/wasm4\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: wasm4\n    recalbox: wasm4\n    retroarch: WASM-4\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: wasm4\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: wasm4\n    romm: wasm-4\n    pretty_name: ''\n  - src: fantasyconsole/microw8\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: MicroW8\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: fantasyconsole/pyxel\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: pyxel\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: fantasyconsole/varvara\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: varvara\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: fantasyconsole/vircon32\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: vircon32\n    recalbox: ''\n    retroarch: Vircon32\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: fantasyconsole/lowresnx\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: lowresnx\n    recalbox: lowresnx\n    retroarch: LowRes NX\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: lowresnx\n    romm: ''\n    pretty_name: ''\n  - src: ferranti/nimrod_computer\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: nimrod\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: nimrod\n    pretty_name: Ferranti Nimrod Computer\n  - src: fujitsu/fm-7\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: fm7\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: fm-7\n    replayfpgaarcade: ''\n    retrodeck: fm7\n    romm: fm-7\n    pretty_name: Fujitsu FM-7\n  - src: fujitsu/fmtowns\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: fmtowns\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: fmtowns\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: fmtowns\n    igdb: fm-towns\n    replayfpgaarcade: ''\n    retrodeck: fmtowns\n    romm: fm-towns\n    pretty_name: Fujitsu FM Towns\n  - src: fujitsu/fmtownsmarty\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Fujitsu FM Towns Marty\n  - src: funtech/superacan\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: supracan\n    recalbox: ''\n    retroarch: Funtech - Super Acan\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: super-acan\n    replayfpgaarcade: ''\n    retrodeck: supracan\n    romm: super-acan\n    pretty_name: Funtech Super ACan\n  - src: gamepark/gp32\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: gp32\n    recalbox: ''\n    retroarch: GamePark - GP32\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: gp32\n    pretty_name: GamePark GP32\n  - src: gamepark/gp2x\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: gp2x\n    pretty_name: GamePark GP2X\n  - src: gamepark/gp2x-wiz\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: gp2x-wiz\n    pretty_name: GamePark GP2X Wiz\n  - src: generalinstruments/ay38500\n    last: other/generalinstrument_ay38500\n    mister: AY-3-8500\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: General Instruments AY38500\n  - src: generalinstruments/champion-2711\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: champion-2711\n    pretty_name: Champion 2711\n  - src: generalinstruments/gimini8600\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: gimini\n    pretty_name: GIMINI 8600\n  - src: generalinstruments/gimini8900\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: GIMINI 8900\n  - src: generalinstruments/gimini8950\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: GIMINI 8950\n  - src: hartung/gamemaster\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: gmaster\n    recalbox: ''\n    retroarch: Hartung - Game Master\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: gmaster\n    romm: hartung\n    pretty_name: Hartung Game Master\n  - src: homelab/homelab\n    last: ''\n    mister: Homelab\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Homelab\n  - src: ibm/pcxt\n    last: ''\n    mister: PCXT\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: pc\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: pc\n    romm: ''\n    pretty_name: IBM PCXT\n  - src: ibm/pcjr\n    last: ''\n    mister: PCjr\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: pc-jr\n    pretty_name: IBM PCjr\n  - src: ibm/5100\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ibm-5100\n    pretty_name: IBM 5100\n  - src: infocom/zmachine\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: zmachine\n    retroarch: Infocom - Z-Machine\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: zmachine\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: zmachine\n    romm: z-machine\n    pretty_name: Infocom Zmachine\n  - src: intelligentsoftware/e128\n    last: ''\n    mister: Enterprise\n    retropie: ''\n    batocera: enterprise\n    recalbox: ''\n    retroarch: Enterprise - 128\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: enterprise\n    pretty_name: Intelligent Software E128\n  - src: sri/500\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: sri-5001000\n    pretty_name: SRI-500/1000\n  - src: intelligentsoftware/e64\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Intelligent Software E64\n  - src: interact/homecomputer\n    last: other/interact_homecomputer\n    mister: Interact\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: interact-model-one\n    pretty_name: Interact Home Computer\n  - src: interton/vc4000\n    last: other/interton_vc4000\n    mister: VC4000\n    retropie: ''\n    batocera: vc4000\n    recalbox: ''\n    retroarch: Interton - VC 4000\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: vc-4000\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: interton-vc-4000\n    pretty_name: Interton VC4000\n  - src: interton/v2000\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: interton-video-2000\n    pretty_name: Interton Video 2000\n  - src: jupiter/ace\n    last: other/jupiter_ace\n    mister: Jupiter\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: jupiter-ace\n    pretty_name: Jupiter ACE\n  - src: leapfrog/clickstart\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: clickstart\n    pretty_name: Leapfrog ClickStart\n  - src: leapfrog/didj\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: didj\n    pretty_name: Leapfrog Didj\n  - src: leapfrog/leapster\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: LeapFrog - Leapster Learning Game System\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: leapster\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: leapster\n    pretty_name: Leapfrog Leapster\n  - src: leapfrog/explorer\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: leapster-explorer-slash-leadpad-explorer\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: leapfrog-explorer\n    pretty_name: Leapfrog Explorer\n  - src: leapfrog/leaptv\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: leaptv\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: leaptv\n    pretty_name: Leapfrog LeapTV\n  - src: magnavox/odyssey\n    last: other/magnavox_odyssey\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: odyssey--1\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: odyssey\n    pretty_name: Magnavox Odyssey\n  - src: magnavox/odyssey2\n    last: other/magnavox_odyssey2\n    mister: ODYSSEY2\n    retropie: ''\n    batocera: odyssey2\n    recalbox: o2em\n    retroarch: Magnavox - Odyssey2\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: odyssey2\n    smdb: ''\n    freestation: ''\n    analoguepocket: odyssey2\n    emudeck: odyssey2\n    igdb: odyssey-2-slash-videopac-g7000\n    replayfpgaarcade: ''\n    retrodeck: odyssey2\n    romm: odyssey-2\n    pretty_name: Magnavox Odyssey 2\n  - src: mattel/aquarius\n    last: ''\n    mister: AQUARIUS\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: 'aquarius'\n    pretty_name: Mattel Aquarius\n  - src: mattel/intellivision\n    last: ''\n    mister: Intellivision\n    retropie: ''\n    batocera: intellivision\n    recalbox: intellivision\n    retroarch: Mattel - Intellivision\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: intellivision\n    smdb: ''\n    freestation: ''\n    analoguepocket: intv\n    emudeck: intellivision\n    igdb: intellivision\n    replayfpgaarcade: ''\n    retrodeck: intellivision\n    romm: intellivision\n    pretty_name: Mattel Intellivision\n  - src: mattel/intellivision_amico\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: intellivision-amico\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: intellivision-amico\n    pretty_name: Mattel Intellivision Amico\n  - src: mattel/hyperscan\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: hyperscan\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: hyperscan\n    pretty_name: Mattel Hyperscan\n  - src: microsoft/msx\n    last: ''\n    mister: MSX\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: msx\n    emudeck: msx\n    igdb: msx\n    replayfpgaarcade: ''\n    retrodeck: msx\n    romm: msx\n    pretty_name: Microsoft MSX\n  - src: microsoft/msx\n    last: ''\n    mister: MSX1\n    retropie: ''\n    batocera: msx1\n    recalbox: msx1\n    retroarch: Microsoft - MSX\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: msx\n    smdb: ''\n    freestation: msx\n    analoguepocket: ''\n    emudeck: msx1\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: msx1\n    romm: ''\n    pretty_name: ''\n  - src: microsoft/msx2\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: msx2\n    recalbox: msx2\n    retroarch: Microsoft - MSX2\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: msx2\n    smdb: ''\n    freestation: msx2\n    analoguepocket: ''\n    emudeck: msx2\n    igdb: msx2\n    replayfpgaarcade: ''\n    retrodeck: msx2\n    romm: msx2\n    pretty_name: Microsoft MSX2\n  - src: microsoft/msxplus\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: msx2+\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: msx2plus\n    pretty_name: Microsoft MSX2+\n  - src: microsoft/msxturbor\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: msxturbor\n    recalbox: msxturbor\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: msxturbor\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: msxturbor\n    romm: msx-turbo\n    pretty_name: Microsoft MSX Turbo R\n  - src: microsoft/xbox\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: xbox\n    recalbox: xbox\n    retroarch: Microsoft - Xbox\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: xbox\n    analoguepocket: ''\n    emudeck: xbox\n    igdb: xbox\n    replayfpgaarcade: ''\n    retrodeck: xbox\n    romm: xbox\n    pretty_name: Microsoft XBOX\n  - src: microsoft/xbox/iso\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Microsoft XBOX (ISO)\n  - src: microsoft/xbox/games\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Microsoft XBOX (Games)\n  - src: microsoft/xbox360\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: xbox360\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: x360\n    analoguepocket: ''\n    emudeck: xbox360\n    igdb: xbox360\n    replayfpgaarcade: ''\n    retrodeck: xbox360\n    romm: xbox360\n    pretty_name: Microsoft Xbox 360\n  - src: microsoft/xbox360/iso\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Microsoft Xbox 360 (ISO)\n  - src: microsoft/xbox360/games\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Microsoft Xbox 360 (Games)\n  - src: microsoft/xboxone\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: xboxone\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: xboxone\n    pretty_name: Microsoft Xbox One\n  - src: microsoft/xbox-series-x\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: series-x-s\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: series-x-s\n    pretty_name: Microsoft Xbox Series-X\n  - src: microsoft/windows\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: windows\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: win\n    replayfpgaarcade: ''\n    retrodeck: windows\n    romm: win\n    pretty_name: Microsoft Windows\n  - src: microsoft/windows/installers\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: windows_installers\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: windows-apps\n    pretty_name: Microsoft Windows (Installers)\n  - src: microsoft/windows/windows3x\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: windows3x\n    romm: win3x\n    pretty_name: Microsoft Windows 3x\n  - src: microsoft/windows/windows9x\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: windows9x\n    romm: ''\n    pretty_name: Microsoft Windows 9x\n  - src: microsoft/dos\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: dos\n    recalbox: dos\n    retroarch: DOS\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: pc\n    smdb: ''\n    freestation: dos\n    analoguepocket: ''\n    emudeck: dos\n    igdb: dos\n    replayfpgaarcade: ''\n    retrodeck: dos\n    romm: dos\n    pretty_name: Microsoft MS-DOS\n  - src: microsoft/windows-mobile\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: windows-mobile\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: windows-mobile\n    pretty_name: Microsoft Windows Mobile\n  - src: microsoft/windows-phone\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: winphone\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: winphone\n    pretty_name: Microsoft Windows Phone\n  - src: microsoft/zune\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: zune\n    pretty_name: Microsoft Zune\n  - src: milesgordon/samcoupe\n    last: other/milesgordon_samcoupe\n    mister: SAMCOUPE\n    retropie: ''\n    batocera: samcoupe\n    recalbox: samcoupe\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: samcoupe\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: samcoupe\n    romm: sam-coupe\n    pretty_name: Miles Gordon Sam Coupe\n  - src: miltonbradley/microvision\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: microvision--1\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: microvision\n    pretty_name: Milton Bradley Microvision\n  - src: mits/altair8800\n    last: other/mits_altair8800\n    mister: Altair8800\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: 'altair-8800'\n    pretty_name: MITS Altair 8800\n  - src: mits/altair680\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: altair-680\n    pretty_name: MITS Altair 680\n  - src: nabu\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: NABU\n  - src: nec/pc88\n    last: ''\n    mister: PC8801\n    retropie: ''\n    batocera: pc88\n    recalbox: pc88\n    retroarch: NEC - PC-8001 - PC-8801\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: pc88\n    igdb: pc-8800-series\n    replayfpgaarcade: ''\n    retrodeck: pc88\n    romm: pc-8800-series\n    pretty_name: NEC PC88\n  - src: nec/pc98\n    last: ''\n    mister: Zet98\n    retropie: ''\n    batocera: pc98\n    recalbox: pc98\n    retroarch: NEC - PC-98\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: pc-9800\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: pc98\n    igdb: pc-9800-series\n    replayfpgaarcade: ''\n    retrodeck: pc98\n    romm: pc-9800-series\n    pretty_name: NEC PC98\n  - src: nec/pcengine\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: pcengine\n    recalbox: pcengine\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: pcengine\n    smdb: Turbo EverDrive & Super SD System 3 SMDB.txt\n    freestation: pce\n    analoguepocket: pce\n    emudeck: pcengine\n    igdb: turbografx-16-slash-pc-engine-cd\n    replayfpgaarcade: ''\n    retrodeck: pcengine\n    romm: ''\n    pretty_name: NEC PC-Engine\n  - src: nec/pcengine\n    last: ''\n    mister: TGFX16\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: NEC - PC Engine - TurboGrafx 16\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: tg16\n    smdb: Turbo EverDrive & Super SD System 3 SMDB.txt\n    freestation: ''\n    analoguepocket: ''\n    emudeck: tg16\n    igdb: turbografx16--1\n    replayfpgaarcade: ''\n    retrodeck: tg16\n    romm: tg16\n    pretty_name: ''\n  - src: nec/pcenginecd\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: pcenginecd\n    recalbox: pcenginecd\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: pcenginecd\n    smdb: PC Engine CD Redump Supplement.txt\n    freestation: pcecd\n    analoguepocket: pcecd\n    emudeck: pcenginecd\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: pcenginecd\n    romm: ''\n    pretty_name: NEC PC-Engine CD\n  - src: nec/pcenginecd\n    last: ''\n    mister: TGFX16-CD\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: NEC - PC Engine CD - TurboGrafx-CD\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: tg16cd\n    smdb: PC Engine CD Redump Supplement.txt\n    freestation: ''\n    analoguepocket: ''\n    emudeck: tg-cd\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: turbografx-cd\n    pretty_name: ''\n  - src: nec/pcengine/supergrafx\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: supergrafx\n    recalbox: supergrafx\n    retroarch: NEC - PC Engine SuperGrafx\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: supergrafx\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: supergrafx\n    igdb: supergrafx\n    replayfpgaarcade: ''\n    retrodeck: supergrafx\n    romm: supergrafx\n    pretty_name: NEX SuperGrafx\n  - src: nec/pcfx\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: pcfx\n    recalbox: pcfx\n    retroarch: NEC - PC-FX\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: pcfx\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: pcfx\n    igdb: pc-fx\n    replayfpgaarcade: ''\n    retrodeck: pcfx\n    romm: pc-fx\n    pretty_name: NEC PCFX\n  - src: nec/pc6000\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: pc60\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: nec-pc-6000-series\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: nec-pc-6000-series\n    pretty_name: NEC PC6000\n  - src: nec/pc6001\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: pc-6001\n    pretty_name: NEC PC6001\n  - src: nec/pc8000\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: pc80\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: pc-8000\n    pretty_name: NEC PC8000\n  - src: nintendo/3ds\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: 3ds\n    recalbox: ''\n    retroarch: Nintendo - Nintendo 3DS\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: 3ds\n    analoguepocket: ''\n    emudeck: ''\n    igdb: 3ds\n    replayfpgaarcade: ''\n    retrodeck: n3ds\n    romm: 3ds\n    pretty_name: Nintendo 3DS\n  - src: nintendo/3ds\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: new-3ds\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: new-nintendo-3ds\n    pretty_name: ''\n  - src: nintendo/ds\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: nds\n    recalbox: nds\n    retroarch: Nintendo - Nintendo DS\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: nds\n    igdb: nds\n    replayfpgaarcade: ''\n    retrodeck: nds\n    romm: nds\n    pretty_name: Nintendo DS\n  - src: nintendo/dsi\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: nds\n    recalbox: nds\n    retroarch: Nintendo - Nintendo DSi\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: nintendo-dsi\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: nintendo-dsi\n    pretty_name: Nintendo DSi\n  - src: nintendo/ereader\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: Nintendo - e-Reader\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: e-reader-slash-card-e-reader\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: e-reader-slash-card-e-reader\n    pretty_name: Nintendo eReader\n  - src: nintendo/famicom\n    last: ''\n    mister: NES\n    retropie: ''\n    batocera: nes\n    recalbox: nes\n    retroarch: Nintendo - Nintendo Entertainment System\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: nes\n    smdb: NES2.0 SMDB.txt\n    freestation: nes\n    analoguepocket: nes\n    emudeck: famicom\n    igdb: famicom\n    replayfpgaarcade: ''\n    retrodeck: famicom\n    romm: famicom\n    pretty_name: Nintendo Famicom\n  - src: nintendo/famicom\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: famicom\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: nes\n    igdb: nes\n    replayfpgaarcade: ''\n    retrodeck: nes\n    romm: nes\n    pretty_name: ''\n  - src: nintendo/famicom/disk\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: fds\n    recalbox: fds\n    retroarch: Nintendo - Family Computer Disk System\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: fds\n    smdb: Famicom Disk System SMDB.txt\n    freestation: ''\n    analoguepocket: ''\n    emudeck: fds\n    igdb: fds\n    replayfpgaarcade: ''\n    retrodeck: fds\n    romm: fds\n    pretty_name: Nintendo Famicom Disk\n  - src: nintendo/gameandwatch\n    last: ''\n    mister: GameNWatch\n    retropie: ''\n    batocera: gameandwatch\n    recalbox: gw\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: gameandwatch\n    smdb: ''\n    freestation: ''\n    analoguepocket: gameandwatch\n    emudeck: gameandwatch\n    igdb: g-and-w\n    replayfpgaarcade: ''\n    retrodeck: gameandwatch\n    romm: g-and-w\n    pretty_name: Nintendo Game & Watch\n  - src: nintendo/gameboy\n    last: ''\n    mister: GAMEBOY\n    retropie: ''\n    batocera: gb\n    recalbox: gb\n    retroarch: Nintendo - Game Boy\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: gb\n    smdb: Game Boy SMDB.txt\n    freestation: ''\n    analoguepocket: gb\n    emudeck: gb\n    igdb: gb\n    replayfpgaarcade: ''\n    retrodeck: gb\n    romm: gb\n    pretty_name: Nintendo Game Boy\n  - src: nintendo/gameboy\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: gb2players\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: nintendo/gameboyadvance\n    last: ''\n    mister: GBA\n    retropie: ''\n    batocera: gba\n    recalbox: gba\n    retroarch: Nintendo - Game Boy Advance\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: gba\n    smdb: EverDrive GBA SMDB.txt\n    freestation: gba\n    analoguepocket: gba\n    emudeck: gba\n    igdb: gba\n    replayfpgaarcade: ''\n    retrodeck: gba\n    romm: gba\n    pretty_name: Nintendo Game Boy Advance\n  - src: nintendo/gameboycolor\n    last: ''\n    mister: GBC\n    retropie: ''\n    batocera: gbc\n    recalbox: gbc\n    retroarch: Nintendo - Game Boy Color\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: gbc\n    smdb: Game Boy Color SMDB.txt\n    freestation: gbc\n    analoguepocket: gbc\n    emudeck: gbc\n    igdb: gbc\n    replayfpgaarcade: ''\n    retrodeck: gbc\n    romm: gbc\n    pretty_name: Nintendo Game Boy Color\n  - src: nintendo/gameboycolor\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: gbc2players\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: nintendo/gamecube/iso\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: gamecube\n    recalbox: gamecube\n    retroarch: Nintendo - GameCube\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: games\n    emuelec: gamecube\n    smdb: ''\n    freestation: ngc\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ngc\n    replayfpgaarcade: ''\n    retrodeck: gc\n    romm: ngc\n    pretty_name: Nintendo - Gamecube (ISO)\n  - src: nintendo/nintendo64\n    last: ''\n    mister: N64\n    retropie: ''\n    batocera: n64\n    recalbox: n64\n    retroarch: Nintendo - Nintendo 64\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: n64\n    smdb: EverDrive 64 & 64drive SMDB.txt\n    freestation: n64\n    analoguepocket: ''\n    emudeck: n64\n    igdb: n64\n    replayfpgaarcade: ''\n    retrodeck: n64\n    romm: n64\n    pretty_name: Nintendo 64\n  - src: nintendo/nintendo64dd\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: n64dd\n    recalbox: 64dd\n    retroarch: Nintendo - Nintendo 64DD\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: n64dd\n    igdb: 64dd\n    replayfpgaarcade: ''\n    retrodeck: n64dd\n    romm: 64dd\n    pretty_name: Nintendo 64DD\n  - src: nintendo/pokemini\n    last: ''\n    mister: PokemonMini\n    retropie: ''\n    batocera: pokemini\n    recalbox: pokemini\n    retroarch: Nintendo - Pokemon Mini\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: pokemini\n    smdb: ''\n    freestation: ''\n    analoguepocket: poke_mini\n    emudeck: pokemini\n    igdb: pokemon-mini\n    replayfpgaarcade: ''\n    retrodeck: pokemini\n    romm: pokemon-mini\n    pretty_name: Nintendo Pokemini\n  - src: nintendo/satellaview\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: satellaview\n    recalbox: satellaview\n    retroarch: Nintendo - Satellaview\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: satellaview\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: satellaview\n    igdb: satellaview\n    replayfpgaarcade: ''\n    retrodeck: satellaview\n    romm: satellaview\n    pretty_name: Nintendo SatellaView\n  - src: nintendo/superfamicom/msu1\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: snes-msu1\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: snesmsu1\n    smdb: ''\n    freestation: snes\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Nintendo Super Famicom (MSU1)\n  - src: nintendo/superfamicom/sufami\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: Nintendo - Sufami Turbo\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: sufami\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: sufami\n    romm: ''\n    pretty_name: Nintendo Super Famicom (Sufami)\n  - src: nintendo/superfamicom\n    last: ''\n    mister: SNES\n    retropie: ''\n    batocera: snes\n    recalbox: snes\n    retroarch: Nintendo - Super Nintendo Entertainment System\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: snes\n    smdb: Super EverDrive & SD2SNES SMDB.txt\n    freestation: snes\n    analoguepocket: snes\n    emudeck: snes\n    igdb: snes\n    replayfpgaarcade: ''\n    retrodeck: snes\n    romm: snes\n    pretty_name: Nintendo Super Famicom\n  - src: nintendo/superfamicom\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: sfc\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: sfc\n    igdb: sfam\n    replayfpgaarcade: ''\n    retrodeck: sfc\n    romm: sfam\n    pretty_name: ''\n  - src: nintendo/supergameboy\n    last: ''\n    mister: SGB\n    retropie: ''\n    batocera: sgb\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: sgb\n    emudeck: sgb\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: sgb\n    romm: ''\n    pretty_name: Nintendo Super Game Boy\n  - src: nintendo/supergameboy/msu1\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: sgb-msu1\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Nintendo Super Game Boy (MSU1)\n  - src: nintendo/virtualboy\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: virtualboy\n    recalbox: virtualboy\n    retroarch: Nintendo - Virtual Boy\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: virtualboy\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: virtualboy\n    igdb: virtualboy\n    replayfpgaarcade: ''\n    retrodeck: virtualboy\n    romm: virtualboy\n    pretty_name: Nintendo Virtual Boy\n  - src: nintendo/virtualconsole\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: vc\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: vc\n    pretty_name: Nintendo Virtual Console\n  - src: nintendo/wii\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: wii\n    recalbox: wii\n    retroarch: Nintendo - Wii\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: wii\n    smdb: ''\n    freestation: wii\n    analoguepocket: ''\n    emudeck: wii\n    igdb: wii\n    replayfpgaarcade: ''\n    retrodeck: wii\n    romm: wii\n    pretty_name: Nintendo Wii\n  - src: nintendo/wiid\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: Nintendo - Wii (Digital)\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: wiiu\n    analoguepocket: ''\n    emudeck: wiiu\n    igdb: wiiu\n    replayfpgaarcade: ''\n    retrodeck: wiiu\n    romm: wiiu\n    pretty_name: Nintendo Wii (Digital)\n  - src: nintendo/wiiu\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: wiiu\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: wiiu\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Nintendo Wii-U\n  - src: nintendo/nintendo-playstation\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: super-nes-cd-rom-system\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: super-nes-cd-rom-system\n    pretty_name: Nintendo PlayStation\n  - src: nichibutsu/myvision\n    last: ''\n    mister: MyVision\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Nichibutsu MyVision\n  - src: nokia/n-gage\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ngage\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ngage\n    replayfpgaarcade: ''\n    retrodeck: ngage\n    romm: ngage\n    pretty_name: Nokia N-Gage\n  - src: nokia/n-gage2\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ngage2\n    pretty_name: Nokia N-Gage (Service)\n  - src: oric/atmos\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: oricatmos\n    recalbox: oricatmos\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: atmos\n    pretty_name: Oric ATMOS\n  - src: oric/tangerine\n    last: ''\n    mister: Oric\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: oric\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: oric\n    romm: ''\n    pretty_name: Oric Tangerine\n  - src: oric/telestrat\n    last: ''\n    mister: TeleStrat\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: oric\n    pretty_name: Oric Telestrat\n  - src: ouya/ouya\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ouya\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ouya\n    pretty_name: OUYA\n  - src: panic/playdate\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: playdate\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: playdate\n    pretty_name: Panic Playdate\n  - src: panasonic/3do\n    last: other/3do\n    mister: ''\n    retropie: ''\n    batocera: 3do\n    recalbox: 3do\n    retroarch: The 3DO Company - 3DO\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: 3do\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: 3do\n    igdb: 3do\n    replayfpgaarcade: ''\n    retrodeck: 3do\n    romm: 3do\n    pretty_name: Panasonic 3DO\n  - src: panasonic/jungle\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: panasonic-jungle\n    pretty_name: Panasonic Jungle\n  - src: pelvarazdin/orao\n    last: other/pelvarazdin_orao\n    mister: ORAO\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: panasonic-jungle\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: orao\n    pretty_name: Pelvarazdin Orao\n  - src: philips/cdi\n    last: ''\n    mister: CD-i\n    retropie: ''\n    batocera: cdi\n    recalbox: cdi\n    retroarch: Philips - CD-i\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: phillips-cdi\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: cdimono1\n    igdb: philips-cdi\n    replayfpgaarcade: ''\n    retrodeck: cdimono1\n    romm: philips-cd-i\n    pretty_name: Philips CDI\n  - src: philips/videopacplus\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: videopacplus\n    recalbox: videopacplus\n    retroarch: Philips - Videopac+\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: videopac\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: videopac\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: videopac\n    romm: videopac-g7400\n    pretty_name: Philips Videopac+\n  - src: philips/vg5000\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: vg5000\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: philips-vg-5000\n    pretty_name: Philips VG5000\n  - src: sega/32x\n    last: ''\n    mister: S32X\n    retropie: ''\n    batocera: sega32x\n    recalbox: sega32x\n    retroarch: Sega - 32X\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: sega32x\n    smdb: ''\n    freestation: 32x\n    analoguepocket: ''\n    emudeck: sega32x\n    igdb: sega32\n    replayfpgaarcade: ''\n    retrodeck: sega32x\n    romm: sega32\n    pretty_name: SEGA 32X\n  - src: sega/aicomputer\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: segaai\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: SEGA AI Computer\n  - src: sega/atomiswave\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: atomiswave\n    recalbox: atomiswave\n    retroarch: Atomiswave\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: atomiswave\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: atomiswave\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: atomiswave\n    romm: ''\n    pretty_name: SEGA Atomiswave\n  - src: sega/advancedpicobeena\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: beena\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: advanced-pico-beena\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: advanced-pico-beena\n    pretty_name: SEGA Advanced Pico Beena\n  - src: sega/chihiro\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: chihiro\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: SEGA Chihiro\n  - src: sega/dreamcast\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: dreamcast\n    recalbox: dreamcast\n    retroarch: Sega - Dreamcast\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: dreamcast\n    smdb: ''\n    freestation: dc\n    analoguepocket: ''\n    emudeck: dreamcast\n    igdb: dc\n    replayfpgaarcade: ''\n    retrodeck: dreamcast\n    romm: dc\n    pretty_name: SEGA Dreamcast\n  - src: sega/dreamcast/vmu\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: vemulator\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: visual-memory-unit-slash-visual-memory-system\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: vmu\n    pretty_name: SEGA Dreamcast VMU\n  - src: sega/gamegear\n    last: ''\n    mister: GameGear\n    retropie: ''\n    batocera: gamegear\n    recalbox: gamegear\n    retroarch: Sega - Game Gear\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: gamegear\n    smdb: EverDrive GG SMDB.txt\n    freestation: gg\n    analoguepocket: gg\n    emudeck: gamegear\n    igdb: gamegear\n    replayfpgaarcade: ''\n    retrodeck: gamegear\n    romm: gamegear\n    pretty_name: SEGA Game Gear\n  - src: sega/hikaru\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: hikaru\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: hikaru\n    pretty_name: SEGA Hikaru\n  - src: sega/mastersystem\n    last: ''\n    mister: SMS\n    retropie: ''\n    batocera: mastersystem\n    recalbox: mastersystem\n    retroarch: Sega - Master System - Mark III\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: mastersystem\n    smdb: Master EverDrive SMDB.txt\n    freestation: sms\n    analoguepocket: sms\n    emudeck: ''\n    igdb: sms\n    replayfpgaarcade: ''\n    retrodeck: mastersystem\n    romm: sms\n    pretty_name: SEGA Master System\n  - src: sega/megacd\n    last: ''\n    mister: MegaCD\n    retropie: ''\n    batocera: megacd\n    recalbox: segacd\n    retroarch: Sega - Mega-CD - Sega CD\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: segacd\n    smdb: SegaCD.txt\n    freestation: mcd\n    analoguepocket: ''\n    emudeck: megacd\n    igdb: sega-cd\n    replayfpgaarcade: ''\n    retrodeck: megacd\n    romm: segacd\n    pretty_name: SEGA Mega CD\n  - src: sega/megacd32x\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: sega-cd-32x\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: segacd32\n    pretty_name: SEGA Mega CD + 32x\n  - src: sega/megadrive\n    last: ''\n    mister: Genesis\n    retropie: ''\n    batocera: megadrive\n    recalbox: megadrive\n    retroarch: Sega - Mega Drive - Genesis\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: genesis\n    smdb: Mega EverDrive SMDB.txt\n    freestation: smd\n    analoguepocket: genesis\n    emudeck: megadrive\n    igdb: genesis-slash-megadrive\n    replayfpgaarcade: ''\n    retrodeck: genesis\n    romm: genesis\n    pretty_name: SEGA Mega Drive\n  - src: sega/megadrive\n    last: ''\n    mister: MegaDrive\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: megadrive\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: genesiswide\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: megadrive\n    romm: ''\n    pretty_name: ''\n  - src: sega/megadrive/msu\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: megadrive-msu\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: megadrivemsu\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: SEGA Mega Drive (MSU)\n  - src: sega/megadrive\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: megadrive-japan\n    smdb: ''\n    freestation: smd\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: sega/model1\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: model1\n    pretty_name: SEGA Model 1\n  - src: sega/model2\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: model2\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: model2\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: model2\n    romm: model2\n    pretty_name: SEGA Model 2\n  - src: sega/model3\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: model3\n    recalbox: model3\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: model3\n    analoguepocket: ''\n    emudeck: model3\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: model3\n    romm: model3\n    pretty_name: SEGA Model 3\n  - src: sega/naomi\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: naomi\n    recalbox: naomi\n    retroarch: Sega - Naomi\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: naomi\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: naomi\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: naomi\n    romm: ''\n    pretty_name: SEGA Naomi\n  - src: sega/naomi2\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: naomi2\n    recalbox: naomi2\n    retroarch: Sega - Naomi 2\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: naomi2\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: naomi2\n    romm: ''\n    pretty_name: SEGA Naomi 2\n  - src: sega/naomigd\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: naomigd\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: naomigd\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: naomigd\n    romm: ''\n    pretty_name: ''\n  - src: sega/pico\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: pico\n    recalbox: pico\n    retroarch: Sega - PICO\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: sega-pico\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: sega-pico\n    pretty_name: SEGA Pico\n  - src: sega/saturn\n    last: ''\n    mister: Saturn\n    retropie: ''\n    batocera: saturn\n    recalbox: saturn\n    retroarch: Sega - Saturn\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: saturn\n    smdb: Saturn Redump Supplement.txt\n    freestation: sat\n    analoguepocket: ''\n    emudeck: saturn\n    igdb: saturn\n    replayfpgaarcade: ''\n    retrodeck: saturn\n    romm: saturn\n    pretty_name: SEGA Saturn\n  - src: sega/sg1000\n    last: ''\n    mister: SG1000\n    retropie: ''\n    batocera: sg1000\n    recalbox: sg1000\n    retroarch: Sega - SG-1000\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: sg-1000\n    smdb: Sega SG-1000 SMDB.txt\n    freestation: sg1000\n    analoguepocket: sg1000\n    emudeck: sg-1000\n    igdb: sg1000\n    replayfpgaarcade: ''\n    retrodeck: sg-1000\n    romm: sg1000\n    pretty_name: SEGA SG-1000\n  - src: sega/triforce\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: triforce\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: triforce\n    romm: ''\n    pretty_name: SEGA Triforce\n  - src: sega/sc3000\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: sc3000\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: sc-3000\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: sc3000\n    pretty_name: SEGA SC-3000\n  - src: sega/systemsp\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: systemsp\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: SEGA System SP\n  - src: sega/stv\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: stv\n    romm: stv\n    pretty_name: SEGA STV\n  - src: sega/system16\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: system16\n    pretty_name: SEGA System 16\n  - src: sega/system32\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: system32\n    pretty_name: SEGA System 32\n  - src: sega/lindbergh\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: lindbergh\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: SEGA Lindbergh\n  - src: sharp/mz\n    last: ''\n    mister: SharpMZ\n    retropie: ''\n    batocera: mz80k\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: sharp-mz-80k7008001500\n    pretty_name: Sharp MZ 80k/700/800/1500\n  - src: sharp/mzb\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: mz2000\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: sharp-mz-80b20002500\n    pretty_name: Sharp MZ-80B/2000/2500\n  - src: sharp/mz2200\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: sharp-mz-2200\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: sharp-mz-2200\n    pretty_name: Sharp MZ-2200\n  - src: sharp/x1\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: x1\n    recalbox: x1\n    retroarch: Sharp - X1\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: x1\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: x1\n    igdb: x1\n    replayfpgaarcade: ''\n    retrodeck: x1\n    romm: x1\n    pretty_name: Sharp X1\n  - src: sharp/x68000\n    last: ''\n    mister: X68000\n    retropie: ''\n    batocera: x68000\n    recalbox: x68000\n    retroarch: Sharp - X68000\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: x68000\n    smdb: ''\n    freestation: x86k\n    analoguepocket: ''\n    emudeck: x68000\n    igdb: sharp-x68000\n    replayfpgaarcade: ''\n    retrodeck: x68000\n    romm: sharp-x68000\n    pretty_name: Sharp X68000\n  - src: sharp/zaurus\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: sharp-zaurus\n    pretty_name: Sharp Zaurus\n  - src: sinclair/ql\n    last: ''\n    mister: QL\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: sinclair-ql\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: sinclair-ql\n    pretty_name: Sinclair QL\n  - src: sinclair/zx80\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: zx80\n    pretty_name: Sinclair ZX80\n  - src: sinclair/zx81\n    last: ''\n    mister: ZX81\n    retropie: ''\n    batocera: zx81\n    recalbox: zx81\n    retroarch: Sinclair - ZX 81\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: zx81\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: zx81\n    igdb: sinclair-zx81\n    replayfpgaarcade: ''\n    retrodeck: zx81\n    romm: zx81\n    pretty_name: Sinclair ZX81\n  - src: sinclair/zxspectrum\n    last: ''\n    mister: Spectrum\n    retropie: ''\n    batocera: zxspectrum\n    recalbox: zxspectrum\n    retroarch: Sinclair - ZX Spectrum\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: zxspectrum\n    smdb: ''\n    freestation: ''\n    analoguepocket: zxspectrum\n    emudeck: zxspectrum\n    igdb: zxs\n    replayfpgaarcade: ''\n    retrodeck: zxspectrum\n    romm: zxs\n    pretty_name: Sinclair ZX Spectrum\n  - src: sinclair/zxspectrum\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: Sinclair - ZX Spectrum +3\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: sinclair/zxspectrum\n    last: ''\n    mister: zx48\n    retropie: ''\n    batocera: zxspectrum\n    recalbox: zxspectrum\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: sinclair/zxspectrum\n    last: ''\n    mister: ZXNext\n    retropie: ''\n    batocera: zxspectrum\n    recalbox: zxspectrum\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: zxnext\n    romm: zx-spectrum-next\n    pretty_name: ''\n  - src: vectrex\n    last: other/vectrex\n    mister: VECTREX\n    retropie: ''\n    batocera: vectrex\n    recalbox: vectrex\n    retroarch: GCE - Vectrex\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: vectrex\n    smdb: Vectrex SMDB.txt\n    freestation: ''\n    analoguepocket: vectrex\n    emudeck: vectrex\n    igdb: vectrex\n    replayfpgaarcade: ''\n    retrodeck: vectrex\n    romm: vectrex\n    pretty_name: Vectrex\n  - src: snk/neogeo\n    last: ''\n    mister: NEOGEO\n    retropie: ''\n    batocera: neogeo\n    recalbox: neogeo\n    retroarch: SNK - Neo Geo\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: neogeo\n    smdb: MiSTer Neo Geo Add-On.txt\n    freestation: neogeo\n    analoguepocket: ng\n    emudeck: neogeo\n    igdb: neogeoaes\n    replayfpgaarcade: ''\n    retrodeck: neogeo\n    romm: neogeoaes\n    pretty_name: SNK NEO-GEO\n  - src: snk/neogeox\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: neo-geo-x\n    pretty_name: SNK NEO-GEO X\n  - src: snk/neogeopocket\n    last: ''\n    mister: NeoGeoPocket\n    retropie: ''\n    batocera: ngp\n    recalbox: ngp\n    retroarch: SNK - Neo Geo Pocket\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ngp\n    smdb: NGPC RetroHQ SMDB.txt\n    freestation: ''\n    analoguepocket: jtngp\n    emudeck: ngp\n    igdb: neo-geo-pocket\n    replayfpgaarcade: ''\n    retrodeck: ngp\n    romm: neo-geo-pocket\n    pretty_name: SNK NEO-GEO Pocket\n  - src: snk/neogeopocketcolor\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ngpc\n    recalbox: ngpc\n    retroarch: SNK - Neo Geo Pocket Color\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ngpc\n    smdb: NGPC RetroHQ SMDB.txt\n    freestation: ''\n    analoguepocket: jtngpc\n    emudeck: ngpc\n    igdb: neo-geo-pocket-color\n    replayfpgaarcade: ''\n    retrodeck: ngpc\n    romm: neo-geo-pocket-color\n    pretty_name: SNK NEO-GEO Pocket Color\n  - src: snk/neogeocd\n    last: ''\n    mister: NeoGeo-CD\n    retropie: ''\n    batocera: neogeocd\n    recalbox: neogeocd\n    retroarch: SNK - Neo Geo CD\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: neocd\n    smdb: ''\n    freestation: neocd\n    analoguepocket: ''\n    emudeck: neogeocd\n    igdb: neo-geo-cd\n    replayfpgaarcade: ''\n    retrodeck: neogeocd\n    romm: neo-geo-cd\n    pretty_name: SNK NEO-GEO CD\n  - src: sony/playstation1\n    last: ''\n    mister: PSX\n    retropie: ''\n    batocera: psx\n    recalbox: psx\n    retroarch: Sony - PlayStation\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: psx\n    smdb: PlayStation Redump Supplement.txt\n    freestation: ps1\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ps\n    replayfpgaarcade: ''\n    retrodeck: psx\n    romm: psx\n    pretty_name: Sony PlayStation\n  - src: sony/playstation1/iso\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: PSXISO\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Sony PlayStation (ISO)\n  - src: sony/playstation1/vcd\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: POPS\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Sony PlayStation (VCD)\n  - src: sony/playstation2\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ps2\n    recalbox: ps2\n    retroarch: Sony - PlayStation 2\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ps2\n    analoguepocket: ''\n    emudeck: ps2\n    igdb: ps2\n    replayfpgaarcade: ''\n    retrodeck: ps2\n    romm: ps2\n    pretty_name: Sony PlayStation 2\n  - src: sony/playstation2/cd\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: CD\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Sony PlayStation 2 (CD)\n  - src: sony/playstation2/dvd\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: DVD\n    ps3netsrv: PS2ISO\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Sony PlayStation 2 (DVD)\n  - src: sony/playstation3\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ps3\n    recalbox: ''\n    retroarch: Sony - PlayStation 3\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ps3\n    analoguepocket: ''\n    emudeck: ps3\n    igdb: ps3\n    replayfpgaarcade: ''\n    retrodeck: ps3\n    romm: ps3\n    pretty_name: Sony PlayStation 3\n  - src: sony/playstation3/games\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: GAMES\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ps3\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Sony PlayStation 3 (Games)\n  - src: sony/playstation3/iso\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: PS3ISO\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Sony PlayStation 3 (ISO)\n  - src: sony/playstation3/psn\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: Sony - PlayStation 3 (PSN)\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Sony PlayStation 3 (PSN)\n  - src: sony/playstation4\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ps4\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ps4\n    igdb: ps4--1\n    replayfpgaarcade: ''\n    retrodeck: ps4\n    romm: ps4\n    pretty_name: Sony PlayStation 4\n  - src: sony/playstation4/pkg\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Sony PlayStation 4 (PKG)\n  - src: sony/playstationportable\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: psp\n    recalbox: psp\n    retroarch: Sony - PlayStation Portable\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: psp\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: psp\n    igdb: psp\n    replayfpgaarcade: ''\n    retrodeck: psp\n    romm: psp\n    pretty_name: Sony PlayStation Portable\n  - src: sony/playstationportable/iso\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: PSPISO\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Sony PlayStation Portable (ISO)\n  - src: sony/playstationportable/minis\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: pspminis\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: psp-minis\n    pretty_name: Sony PlayStation Portable (Minis)\n  - src: sony/playstationportable/psn\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: Sony - PlayStation Portable (PSN)\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Sony PlayStation Portable (PSN)\n  - src: sony/playstationvita\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: psvita\n    recalbox: ''\n    retroarch: Sony - PlayStation Vita\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: psvita\n    igdb: psvita\n    replayfpgaarcade: ''\n    retrodeck: psvita\n    romm: psvita\n    pretty_name: Sony PlayStation Vita\n  - src: sony/pocketstation\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: pocketstation\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: pocketstation\n    pretty_name: Sony PocketStation\n  - src: sony/smc777\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: smc-777\n    pretty_name: Sony SMC-777\n  - src: sord/m5\n    last: other/sord_m5\n    mister: Sord M5\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: sord-m5\n    pretty_name: Sord M5\n  - src: spectravideo/sv328\n    last: other/spectravideo_sv328\n    mister: SVI328\n    retropie: ''\n    batocera: spectravideo\n    recalbox: spectravideo\n    retroarch: Spectravideo - SVI-318 - SVI-328\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: spectravideo\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: spectravideo\n    romm: spectravideo\n    pretty_name: SpectraVideo SV328\n  - src: tandy/vis\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: vis\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: tandy-vis\n    pretty_name: Tandy VIS\n  - src: tandy/trs80\n    last: ''\n    mister: TRS-80\n    retropie: ''\n    batocera: trs80\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: trs-80\n    igdb: trs-80\n    replayfpgaarcade: ''\n    retrodeck: trs-80\n    romm: trs-80\n    pretty_name: Tandy TRS-80\n  - src: tandy/trs80colorcomputer1\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: coco\n    recalbox: trs80coco\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: coco\n    igdb: trs-80-color-computer\n    replayfpgaarcade: ''\n    retrodeck: coco\n    romm: trs-80-color-computer\n    pretty_name: Tandy Color Computer 1\n  - src: tandy/trs80colorcomputer2\n    last: ''\n    mister: CoCo2\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Tandy Color Computer 2\n  - src: tandy/trs80colorcomputer3\n    last: ''\n    mister: COCO3\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Tandy Color Computer 3\n  - src: tandy/trs80mc10\n    last: ''\n    mister: AliceMC10\n    retropie: ''\n    batocera: mc10\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: trs-80-mc-10\n    pretty_name: Tandy TRS-80 MC10\n  - src: tandy/trs80model100\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: trs-80-model-100\n    pretty_name: Tandy TRS-80 Model 100\n  - src: tandy/1000\n    last: ''\n    mister: Tandy1000\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Tandy 1000\n  - src: tapwave/zodiac\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: zod\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: zodiac\n    pretty_name: Tapwave Zodiac\n  - src: tatung/einstein\n    last: other/tatung_einstein\n    mister: TatungEinstein\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: tatung-einstein\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: tatung-einstein\n    pretty_name: Tatung Einstein\n  - src: telstar/ay-3-8500\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ay-3-8500\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ay-3-8500\n    pretty_name: Telstar AY-3-8500\n  - src: telstar/ay-3-8603\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ay-3-8603\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ay-3-8603\n    pretty_name: Telstar AY-3-8603\n  - src: telstar/ay-3-8605\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ay-3-8605\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ay-3-8605\n    pretty_name: Telstar AY-3-8605\n  - src: telstar/ay-3-8606\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ay-3-8606\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ay-3-8606\n    pretty_name: Telstar AY-3-8606\n  - src: telstar/ay-3-8607\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ay-3-8607\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ay-3-8607\n    pretty_name: Telstar AY-3-8607\n  - src: telstar/ay-3-8610\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ay-3-8610\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ay-3-8610\n    pretty_name: Telstar AY-3-8610\n  - src: telstar/ay-3-8710\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ay-3-8710\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ay-3-8710\n    pretty_name: Telstar AY-3-8710\n  - src: telstar/ay-3-8760\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ay-3-8760\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ay-3-8760\n    pretty_name: Telstar AY-3-8760\n  - src: tesla/ondraspo186\n    last: other/tesla_ondraspo186\n    mister: Ondra_SPO186\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Tesla Ondraspo18\n  - src: tesla/pmd85\n    last: other/tesla_pmd85\n    mister: PMD85\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Tesla PMD85\n  - src: texas_instruments/ti82\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ti-82\n    pretty_name: Texas Instruments TI-82\n  - src: texas_instruments/ti83\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ti-83\n    pretty_name: Texas Instruments TI-83\n  - src: texas_instruments/ti99\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ti99\n    recalbox: ti994a\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ti99\n    igdb: ti-99\n    replayfpgaarcade: ''\n    retrodeck: ti99\n    romm: ti-99\n    pretty_name: Texas Instruments TI-99\n  - src: texas_instruments/ti994a\n    last: ''\n    mister: TI-99_4A\n    retropie: ''\n    batocera: ''\n    recalbox: ti994a\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ti-994a\n    pretty_name: Texas Instruments TI-99/4A\n  - src: thomson/thomson\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: thomson\n    recalbox: thomson\n    retroarch: Thomson - MOTO\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: moto\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: moto\n    romm: thomson-to\n    pretty_name: Thomson\n  - src: thomson/to8\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: to8\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: to8\n    romm: ''\n    pretty_name: Thomson TO8\n  - src: thomson/mo5\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: thomson-mo5\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: thomson-mo5\n    pretty_name: Thomson MO5\n  - src: tiger/gamecom\n    last: gamecom\n    mister: ''\n    retropie: ''\n    batocera: gamecom\n    recalbox: ''\n    retroarch: Tiger - Game.com\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: gamecom\n    igdb: game-dot-com\n    replayfpgaarcade: ''\n    retrodeck: gamecom\n    romm: game-dot-com\n    pretty_name: Tiger Game.com\n  - src: tiger/r-zone\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: r-zone\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: r-zone\n    pretty_name: Tiger R-Zone\n  - src: timetop/gameking\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: game_king\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Timetop GameKing\n  - src: tomytronic/scramble\n    last: ''\n    mister: TomyScramble\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: TomyTronic Scramble\n  - src: tomytronic/tutor\n    last: ''\n    mister: TomyTutor\n    retropie: ''\n    batocera: tutor\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: tomy-tutor-slash-pyuta-slash-grandstand-tutor\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: tomy-tutor\n    pretty_name: TomyTronic Tutor\n  - src: tsukuda/othellomultivision\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: multivision\n    recalbox: multivision\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: multivision\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: multivision\n    romm: multivision\n    pretty_name: Tsukuda Othello Multivision\n  - src: umtech/videobrain\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: videobrain\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: videobrain\n    pretty_name: Umtech VideoBrain\n  - src: rca/cosmac\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: fred-cosmac\n    pretty_name: COSMAC\n  - src: rca/cosmacelf\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: COSMAC Elf\n  - src: rca/cosmacvip\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: COSMAC VIP\n  - src: researchmachines/380z\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: research-machines-380z\n    pretty_name: Research Machines 380Z\n  - src: rca/studioii\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: RCA - Studio II\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: studio2\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: rca-studio-ii\n    pretty_name: RCA Studio II\n  - src: vmlabs/nuon\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: nuon\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: nuon\n    pretty_name: VMLabs Nuon\n  - src: vector/06c\n    last: other/vector06c\n    mister: VECTOR06\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: 06c\n    pretty_name: Vector 06c\n  - src: videos/bdiso\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: BDISO\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Videos BD\n  - src: videos/dvdiso\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: DVDISO\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Videos DVD\n  - src: videotechnology/creativision\n    last: vtech/creativision\n    mister: CreatiVision\n    retropie: ''\n    batocera: crvision\n    recalbox: ''\n    retroarch: VTech - CreatiVision\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: creativision\n    emudeck: crvision\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: crvision\n    romm: creativision\n    pretty_name: VTech CreatiVision\n  - src: videotechnology/vtechlaser200\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: laser200\n    pretty_name: VTech Laser 200\n  - src: videotechnology/vtechlaser\n    last: other/videotechnology_vtechlaser\n    mister: Laser\n    retropie: ''\n    batocera: laser310\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: VTech Laser\n  - src: videotechnology/vsmile\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: vsmile\n    recalbox: ''\n    retroarch: VTech - V.Smile\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: vsmile\n    igdb: vsmile\n    replayfpgaarcade: ''\n    retrodeck: vsmile\n    romm: vsmile\n    pretty_name: VTech vSmile\n  - src: videotechnology/socrates\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: socrates\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: socrates\n    pretty_name: VTech Socrates\n  - src: videotechnology/vflash\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: vflash\n    pretty_name: VTech V.Flash\n  - src: videoton/tvcomputer\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: tvc\n    recalbox: ''\n    retroarch: Videoton - TV-Computer\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Videoton TV-Computer\n  - src: watara/supervision\n    last: ''\n    mister: SuperVision\n    retropie: ''\n    batocera: supervision\n    recalbox: supervision\n    retroarch: Watara - Supervision\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: supervision\n    smdb: Watara Supervision SMDB.txt\n    freestation: ''\n    analoguepocket: supervision\n    emudeck: supervision\n    igdb: watara-slash-quickshot-supervision\n    replayfpgaarcade: ''\n    retrodeck: supervision\n    romm: supervision\n    pretty_name: Watara Supervision\n  - src: bandai/supervision8000\n    last: ''\n    mister: SuperVision8000\n    retropie: ''\n    batocera: sv8000\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: super-vision-8000\n    pretty_name: Super Vision 800\n  - src: pebble\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: pebble\n    pretty_name: Pebble Watch\n  - src: pinball/fpinball\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: fpinball\n    romm: ''\n    pretty_name: Future Pinball\n  - src: pinball/vpinball\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: vpinball\n    recalbox: vpinball\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: vpinball\n    romm: pinball\n    pretty_name: Virtual Pinball\n  - src: plex/arcade\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: plex-arcade\n    pretty_name: Plex Arcade\n  - src: pokitto\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: pokitto\n    pretty_name: pokitto\n  - src: polymorphicsystems/poly-88\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: poly-88\n    pretty_name: Poly 88\n  - src: polymorphicsystems/system-8813\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Polymega\n  - src: playmaji/polymega\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: polymega\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: polymega\n    pretty_name: Polymega\n  - src: capcom/cps1\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: cps1\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: cps1\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: cps1\n    romm: cps1\n    pretty_name: Capcom CPS-1\n  - src: capcom/cps2\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: cps2\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: cps2\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: cps2\n    romm: cps2\n    pretty_name: Capcom CPS-2\n  - src: capcom/cps3\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: cps3\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: cps3\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: cps3\n    romm: cps3\n    pretty_name: Capcom CPS-3\n  - src: symbian/symbianos\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: symbian\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: symbian\n    romm: symbian\n    pretty_name: SymbianOS\n  - src: palm/palmos\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: palm\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: palm\n    igdb: palm-os\n    replayfpgaarcade: ''\n    retrodeck: palm\n    romm: palm-os\n    pretty_name: Palm OS\n  - src: zeebo/zeebo\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: zeebo\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: zeebo\n    pretty_name: Zeebo\n  - src: zpa/iq151\n    last: ''\n    mister: IQ151\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: IQ151\n  - src: dist/image\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: dist/iso\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: dist/project\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: engine/cannonball\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: cannonball\n    recalbox: outrun\n    retroarch: Cannonball\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Cannonball\n  - src: engine/devilutionx\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: devilutionx\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: DevolutionX\n  - src: engine/dxx\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: dxx-rebirth\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: DXX\n  - src: engine/easyrpg\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: easyrpg\n    recalbox: easyrpg\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: easyrpg\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: easyrpg\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: easyrpg\n    romm: ''\n    pretty_name: EasyRPG\n  - src: engine/eduke\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: eduke32\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: EDuke\n  - src: engine/etlegacy\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: etlegacy\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ETLegacy\n  - src: engine/fury\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: fury\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Fury\n  - src: engine/ikemen\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ikemen\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ikemen\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: IKEMen\n  - src: engine/mugen\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: mugen\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: mugen\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: mugen\n    romm: mugen\n    pretty_name: Mugen\n  - src: engine/openbor\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: openbor\n    recalbox: openbor\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: openbor\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: openbor\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: openbor\n    romm: openbor\n    pretty_name: OpenBOR\n  - src: engine/openlara\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: OpenLara\n  - src: engine/puzzlescript\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: PuzzleScript\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Puzzle Script\n  - src: engine/raze\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: raze\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Raze\n  - src: engine/rpgmaker\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: RPG Maker\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: RPG Maker\n  - src: engine/smbx\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: thextech\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: SMBX\n  - src: engine/solarus\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: solarus\n    recalbox: solarus\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: solarus\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: solarus\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: solarus\n    romm: ''\n    pretty_name: Solarus\n  - src: engine/sonicretro\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: sonicretro\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Sonic Retro\n  - src: engine/stratagus\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: stratagus\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Stratagus\n  - src: engine/theforceengine\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: theforceengine\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Force Engine\n  - src: engine/xash3d\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: XHash3D\n  - src: idstuff/catacomb\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: catacomb\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: idstuff/doom\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: gzdoom\n    recalbox: doom\n    retroarch: DOOM\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: doom\n    analoguepocket: ''\n    emudeck: doom\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: doom\n    romm: ''\n    pretty_name: DOOM\n  - src: idstuff/doom3\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: doom3\n    recalbox: doom3\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: DOOM3\n  - src: idstuff/ecwolf\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ecwolf\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ecwolf\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ECWolf\n  - src: idstuff/prboom\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: prboom\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: prboom\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: PRBOOM\n  - src: idstuff/quake\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: quake\n    recalbox: quake\n    retroarch: Quake\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: quake\n    analoguepocket: ''\n    emudeck: quake\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: quake\n    romm: ''\n    pretty_name: Quake\n  - src: idstuff/quake2\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: quake2\n    recalbox: quake2\n    retroarch: Quake II\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Quake 2\n  - src: idstuff/quake3\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: quake3\n    recalbox: quake3\n    retroarch: Quake III\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Quake 3\n  - src: idstuff/rtcw\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: rtcw\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: RTCW\n  - src: idstuff/wolfenstein3d\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: wolfenstein3d\n    retroarch: Wolfenstein 3D\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Wolf3D\n  - src: media/vgmplay\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: vgmplay\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: VGMPlay\n  - src: media/karaoke\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: karaoke\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: media/mplayer\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: mplayer\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: media/imageviewer\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: imageviewer\n    recalbox: imageviewer\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: imageviewer\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: media/library\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: library\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: media/screenshots\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: media/recordings\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: recordings\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: other/arduboy\n    last: ''\n    mister: Arduboy\n    retropie: ''\n    batocera: arduboy\n    recalbox: arduboy\n    retroarch: Arduboy Inc - Arduboy\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: arduboy\n    emudeck: arduboy\n    igdb: arduboy\n    replayfpgaarcade: ''\n    retrodeck: arduboy\n    romm: arduboy\n    pretty_name: Arduboy\n  - src: other/chip8\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: CHIP-8\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: CHIP-8\n  - src: other/flash\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: flash\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: flash\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: flash\n    romm: ''\n    pretty_name: Flash\n  - src: other/flashback\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: reminiscence\n    recalbox: flashback\n    retroarch: Flashback\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: other/galaksija\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: galaksija\n    pretty_name: Galaksija\n  - src: other/games\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: other/intel_x86\n    last: ''\n    mister: AO486\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: pc\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Intel X86\n  - src: other/pc-50x-family\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: pc-50x-family\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: pc-50x-family\n    pretty_name: PC-50x-family\n  - src: other/radio_86rk\n    last: ''\n    mister: APOGEE\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Radio 86RK\n  - src: other/scummvm\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: scummvm\n    recalbox: scummvm\n    retroarch: ScummVM\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: scummvm\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: scummvm\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: scummvm\n    romm: scummvm\n    pretty_name: ScummVM\n  - src: other/searle_multicomp\n    last: ''\n    mister: MultiComp\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Searle Multicomp\n  - src: other/specialist_mx\n    last: ''\n    mister: SPMX\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Specialist MX\n  - src: other/zxevolution\n    last: ''\n    mister: TSConf\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ZX Evolution\n  - src: platform/chailove\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ChaiLove\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: chailove\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: chailove\n    romm: ''\n    pretty_name: ChailLove\n  - src: platform/amazon/fire-tv\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: firetv\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: amazon-fire-tv\n    pretty_name: ''\n  - src: platform/android\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: android\n    igdb: android\n    replayfpgaarcade: ''\n    retrodeck: android\n    romm: android\n    pretty_name: Android\n  - src: platform/android/apps\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: androidapps\n    romm: ''\n    pretty_name: Android (Apps)\n  - src: platform/android/games\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: androidgames\n    romm: ''\n    pretty_name: Android (Games)\n  - src: platform/blackberry\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: blackberry\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: blackberry\n    pretty_name: Blackberry\n  - src: platform/browser\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: browser\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: browser\n    pretty_name: ''\n  - src: platform/cpm\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: cpm\n    pretty_name: CP/M\n  - src: platform/doja\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: doja\n    pretty_name: DoJa\n  - src: platform/dangeros\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: danger-os\n    pretty_name: Danger OS\n  - src: platform/evercade\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: evercade\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: evercade\n    pretty_name: Evercade\n  - src: platform/exen\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: exen\n    pretty_name: ExEn\n  - src: platform/gamemaker\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: gmloader\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Gamemaker\n  - src: platform/glulx\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: glulx\n    pretty_name: Glulx\n  - src: platform/google/stadia\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: stadia\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: stadia\n    pretty_name: Google Stadia\n  - src: platform/handheld\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: Handheld Electronic Game\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: handheld\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: handheld-electronic-lcd\n    pretty_name: Handheld\n  - src: platform/ios\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ios\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ios\n    pretty_name: iOS\n  - src: platform/java\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: j2me\n    recalbox: ''\n    retroarch: Mobile - J2ME\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: freej2me\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: j2me\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: j2me\n    romm: j2me\n    pretty_name: Java\n  - src: platform/kaios\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: kaios\n    pretty_name: KaiOS\n  - src: platform/lcdgames\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: lcdgames\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: lcdgames\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: lcdgames\n    romm: ''\n    pretty_name: LCD Games\n  - src: platform/legacy-computer\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: legacy-computer\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: legacy-computer\n    pretty_name: ''\n  - src: platform/linux\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: linux\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: linux\n    pretty_name: ''\n  - src: platform/maemo\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: maemo\n    pretty_name: Maemo\n  - src: platform/meego\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: meego\n    pretty_name: MeeGo\n  - src: platform/mobile\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: mobile\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: mobile\n    pretty_name: ''\n  - src: platform/mophun\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: mophun\n    pretty_name: Mophun\n  - src: platform/mre\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: mre\n    pretty_name: MediaTek MRE\n  - src: platform/onlive-game-system\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: onlive\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: onlive-game-system\n    pretty_name: ''\n  - src: platform/os2\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: os2\n    pretty_name: IBM OS/2\n  - src: platform/pc-booter\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: pc-booter\n    pretty_name: PC Booter\n  - src: platform/plugnplay\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: tvgames\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: plug-and-play\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: plug-and-play\n    pretty_name: ''\n  - src: platform/pygame\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: pygame\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: PyGame\n  - src: platform/steam\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: steam\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: platform/thepowdertoy\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: thepowdertoy\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Powder Toy\n  - src: platform/gvm\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: gvm\n    pretty_name: GVM\n  - src: platform/scmp\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: scmp\n    pretty_name: SC/MP\n  - src: platform/signetics2650\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: signetics-2650\n    pretty_name: Signetics 2650\n  - src: platform/skvm\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: sk-vm\n    pretty_name: SK-VM\n  - src: platform/tads\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: tads\n    pretty_name: TADS Interpreter\n  - src: platform/zinc\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: zinc\n    pretty_name: ZiNc Interpreter\n  - src: platform/gnex\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: gnex\n    pretty_name: GNEX\n  - src: platform/hugo\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: hugo\n    pretty_name: Hugo\n  - src: ports\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ports\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ports\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ports\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ports\n    romm: ''\n    pretty_name: ''\n  - src: ports/2048\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: '2048'\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/abuse\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: abuse\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/bomberman\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: mrboom\n    recalbox: mrboom\n    retroarch: MrBoom\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/cave3rd\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: cave3rd\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/cavestory\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: cavestory\n    recalbox: cavestory\n    retroarch: Cave Story\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: cavestory\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/cdogs\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: cdogs\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/commanderkeen\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: cgenius\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/corsixth\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: corsixth\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/dinothawr\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: dinothawr\n    retroarch: Dinothawr\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/dungeoncrawlstonesoup\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: dungeoncrawlstonesoup\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/fallout1\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: fallout1-ce\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/fallout2\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: fallout2-ce\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/flappybird\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: flappybird\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/hcl\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: hcl\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/hurrican\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: hurrican\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/jazzjackrabbit\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: openjazz\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/jazzjackrabbit2\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: jazz2\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/jumpnbump\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: \"Jump 'n Bump\"\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/lutro\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: lutro\n    recalbox: lutro\n    retroarch: Lutro\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: lutro\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: lutro\n    romm: ''\n    pretty_name: ''\n  - src: ports/minecraft\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: minecraft\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/pong\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: gong\n    recalbox: gong\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: pong\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/princeofpersia\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: sdlpop\n    recalbox: princeofpersia\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/rickdangerous\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: xrick\n    recalbox: rickdangerous\n    retroarch: Rick Dangerous\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/sonic3\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: sonic3-air\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/sonicmania\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: sonic-mania\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/superbroswar\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: superbroswar\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/tombraider\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: traider1\n    recalbox: tombraider\n    retroarch: Tomb Raider\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/tombraider2\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: traider2\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/tyrian\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: tyrian\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/urquan-masters\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: uqm\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/zeldaclassic\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: zc210\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: tools/240ptestsuite\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: 240ptestsuite\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: 240p Test Suite\n  - src: tools/flatpak\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: flatpak\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: tools/moonlight\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: moonlight\n    recalbox: moonlight\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: moonlight\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: tools/odcommander\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: odcommander\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: other/lightgun\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: namco2x6\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: technosys_research_labs/aamber-pegasus\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: pegasus\n    pretty_name: 'Aamber Pegasus'\n  - src: abc/80\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: abc-80\n    pretty_name: 'ABC 80'\n  - src: 'worlds-of-wonder/action-max'\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: action-max\n    pretty_name: WoW Action Max\n  - src: matra-and-hachetter-ordinateur/alice\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: alice-3290\n    pretty_name: 'Alice 32/90'\n  - src: ports/halflife\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: halflife\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: 'Half Life'\n  - src: other/laseractive\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: laseractive\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: laseractive\n    pretty_name: 'LaserActive'\n  - src: other/blacknut\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: blacknut\n    pretty_name: Blacknut\n  - src: computer_power_and_light/compal80\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: compal-80\n    pretty_name: Compal 80\n  - src: compucolor/compucolor8001\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: compucolor-i\n    pretty_name: Compucolor 8001\n  - src: compucolor/compucolorii\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: compucolor-ii\n    pretty_name: Compucolor II\n  - src: compucorp/100-series\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: compucorp-programmable-calculator\n    pretty_name: Compucorp 100 Series Calculator\n  - src: compucorp/200-series\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Compucorp 200 Series Calculator\n  - src: compucorp/300-series\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Compucorp 300 Series Calculator\n  - src: compucorp/400-series\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Compucorp 400 Series Calculator\n  - src: cybervision/2001\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: cybervision\n    pretty_name: CyberVision 2001\n  - src: grey_innovation/digiblast\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: digiblast\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: digiblast\n    pretty_name: digiBlast\n  - src: ecd/micromind\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ecd-micromind\n    pretty_name: ECD Micromind\n  - src: bgr/excalibur64\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: excalibur-64\n    pretty_name: Excalibur 64\n  - src: exelvision/exl100\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: exelvision\n    pretty_name: Exelvision EXL100\n  - src: zapitigames/gamewave\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: game-wave\n    pretty_name: Game Wave\n  - src: playjam/gamestick\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: gamestick\n    pretty_name: GameStick\n  - src: tigertelmatics/gizmondo\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: gizmondo\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: gizmondo\n    pretty_name: Gizmondo\n  - src: heathkit/h11\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: heathkit-h11\n    pretty_name: Heathkit H11\n  - src: heathkit/h8\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: heathzenith\n    pretty_name: Heath/Zenith H8/H89\n  - src: hitachi/s1\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: hitachi-s1\n    pretty_name: Hitachi S1\n  - src: hp/hp-9800\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: hp-9800\n    pretty_name: HP 9800\n  - src: hp/hp3000\n    last: hp3000\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: hp3000\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: hp3000\n    pretty_name: HP 3000\n  - src: hp/hp2100\n    last: hp2100\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: hp2100\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: hp2100\n    pretty_name: HP 2100\n  - src: iircade\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: iircade\n    pretty_name: iiRcade\n  - src: imlac/pds1\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: imlac-pds1\n    pretty_name: Imlac PDS-1\n  - src: jolt\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: jolt\n    pretty_name: Jolt\n  - src: mos/kim1\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: kim-1\n    pretty_name: KIM-1\n  - src: matsushita/jr\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: matsushitapanasonic-jr\n    pretty_name: Matsushita/Panasonic JR\n  - src: memotech/mtx\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: memotech-mtx\n    pretty_name: Memotech MTX\n  - src: memotech/mtx512\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: mtx512\n    pretty_name: Memotech MTX512\n  - src: meraelzab/meritum\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: meritum\n    pretty_name: Mera-Elzab Meritum\n  - src: appliedtechnology/microbee\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: microbee\n    pretty_name: Microbee\n  - src: tangeringcomputersystems/microtan65\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: microtan-65\n    pretty_name: Microtan 65\n  - src: nascom/1\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: nascom\n    pretty_name: Nascom 1\n  - src: grundybusinesssystem/newbrain\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: newbrain\n    pretty_name: NewBrain\n  - src: northstarcomputers/northstar\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: northstar\n    pretty_name: NorthStar\n  - src: noval/760\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: noval-760\n    pretty_name: Noval 760\n  - src: ohioscientific\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ohio-scientific\n    pretty_name: Ohio Scientific\n  - src: namco/system22\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: system-32\n    pretty_name: Namco System 22\n  - src: soundic/sd2x0\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: sd-200270290\n    pretty_name: Soundic Soundicvision SD-200/270/290\n  - src: taito/type-x\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: type-x\n    pretty_name: Taito Type X\n  - src: taito/x55\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: taito-x-55\n    pretty_name: Taito X-55\n  - src: tektronix/4050\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: tektronix-4050\n    pretty_name: Tektronix 4050\n  - src: nvphilipsgloeilampenfabrieken/telespieles2201\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: tele-spiel\n    pretty_name: Tele-Spiel ES-2201\n  - src: tikidata/tiki100\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: tiki-100\n    pretty_name: Tiki-100\n  - src: timex/sinclair2068\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: timex-sinclair-2068\n    pretty_name: Timex Sinclair 2068\n  - src: fuze/tomahawkf1\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: tomahawk-f1\n    pretty_name: Tomahawk F1\n  - src: computerdatasystems/versatile\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: versatile\n    pretty_name: Versatile\n  - src: wang/2200\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: wang2200\n    pretty_name: Wang 2200\n  - src: ssd/xavixport\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: xavixport\n    pretty_name: XaviXPORT\n  - src: xerox/alto\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: xerox-alto\n    pretty_name: Xerox Alto\n  - src: transam/triton\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: triton\n    pretty_name: Transam Triton\n  - src: samsung/tizen\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: tizen\n    pretty_name: Samsung Tizen\n  - src: samsung/bada\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: bada\n    pretty_name: Samsung Bada\n  - src: idealcomputer\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ideal-computer\n    pretty_name: Ideal-Computer\n  - src: platform/brew\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: brew\n    pretty_name: BREW\n  - src: brightthings/bubble\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: bubble\n    pretty_name: Bright Things Bubble\n  - src: micronique/hector1\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Hector 1\n  - src: micronique/hector2hr\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Hector 2 HR\n  - src: micronique/hector2hrplus\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Hector 2HR+\n  - src: micronique/hectorhrx\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: hrx\n    pretty_name: Hector HRX\n  - src: micronique/hectormx\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Hector MX\n  - src: superfunfun/sureshothd\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: sure-shot-hd\n    pretty_name: Sure Shot HD\n  - src: gakken/compactvisiontvboy\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ctvboy\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: Compact Vision TV Boy\n\nsystem_unsupported:\n  - src: dvd-player\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: dvd-player\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: dvd-player\n    pretty_name: ''\n  - src: hd-dvd-player\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: hd-dvd-player\n    pretty_name: ''\n  - src: blu-ray-player\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: blu-ray-player\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: blu-ray-player\n    pretty_name: ''\n  - src: meta-quest-2\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: meta-quest-2\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: meta-quest-2\n    pretty_name: ''\n  - src: meta-quest-3\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: meta-quest-3\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: meta-quest-3\n    pretty_name: ''\n  - src: oculus-go\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: oculus-go\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: oculus-go\n    pretty_name: ''\n  - src: oculus-quest\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: oculus-quest\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: oculus-quest\n    pretty_name: ''\n  - src: oculus-rift\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: oculus-rift\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: oculus-rift\n    pretty_name: ''\n  - src: sony/playstation-vr\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: psvr\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: psvr\n    pretty_name: ''\n  - src: sony/psvr2\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: psvr2\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: psvr2\n    pretty_name: ''\n  - src: gear-vr\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: gear-vr\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: gear-vr\n    pretty_name: ''\n  - src: airconsole\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: airconsole\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: airconsole\n    pretty_name: ''\n  - src: donner30\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: donner30\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: donner30\n    pretty_name: ''\n  - src: ooparts\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ooparts\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ooparts\n    pretty_name: ''\n  - src: sol-20\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: sol-20\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: sol-20\n    pretty_name: ''\n  - src: plato\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: plato--1\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: plato\n    pretty_name: ''\n  - src: sdssigma7\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: sdssigma7\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: sdssigma7\n    pretty_name: ''\n  - src: edsac\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: edsac--1\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: edsac\n    pretty_name: ''\n  - src: sri/500\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: sri-5001000\n    pretty_name: SRI-500/1000\n  - src: swtpc/6800\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: swtpc-6800\n    pretty_name: SWTPC 6800\n  - src: timeshare\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: call-a-computer\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: call-a-computer\n    pretty_name: ''\n  - src: pcv2\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: pcv2\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: startrekvoyager\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: startrekvoyager\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: vvvvvv\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: vvvvvv\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: 8bit-productions/commanderx16\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: commanderx16\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: commander-x16\n    pretty_name: 'Commander X16'\n  - src: ports/rott\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: rott\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/bstone\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: bstone\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/jkdf2\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: jkdf2\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/jknight\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: jknight\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/mohaa\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: mohaa\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/bennugd\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: bennugd\n    recalbox: ''\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/julius\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: caesar3\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: ''\n  - src: ports/corsixth\n    last: ''\n    mister: ''\n    retropie: ''\n    batocera: ''\n    recalbox: themehospital\n    retroarch: ''\n    ops2l: ''\n    ps3netsrv: ''\n    fspd: ''\n    emuelec: ''\n    smdb: ''\n    freestation: ''\n    analoguepocket: ''\n    emudeck: ''\n    igdb: ''\n    replayfpgaarcade: ''\n    retrodeck: ''\n    romm: ''\n    pretty_name: 'Theme Hospital'\n"
  },
  {
    "path": "ansible/retronas_update_user.yml",
    "content": "---\n- hosts: localhost\n  gather_facts: false\n\n  tasks:\n    - name: \"Load RetroNAS config\"\n      ansible.builtin.include_vars: retronas_vars.yml\n\n    - name: \"update user\"\n      ansible.builtin.user:\n        user: \"{{ retronas_user }}\"\n        groups: \"disk\"\n        append: true\n"
  },
  {
    "path": "ansible/retronas_vars.yml.default",
    "content": "retronas_path: \"/data/retronas\"\nretronas_user: \"retronas\"\nretronas_group: \"retronas\"\nretronas_root: \"/opt/retronas\"\nretronas_net_retro_interface: \"eth0\"\nretronas_net_retro_ip: \"10.99.1.1\"\nretronas_net_retro_subnet: \"23\"\nretronas_net_retro_dhcprange: \"10.99.1.150,10.99.1.200,6h\"\nretronas_net_retro_router: \"10.99.1.1\"\nretronas_net_retro_ntp: \"10.99.1.1\"\nretronas_net_retro_dns: \"10.99.1.1\"\nretronas_net_modern_interface: \"wlan0\"\nretronas_etherdfs_interface: \"eth0\"\nretronas_net_upstream_dns1: \"8.8.8.8\"\nretronas_net_upstream_dns2: \"8.8.4.4\"\nretronas_net_wifi_interface: \"wlan0\"\nretronas_net_wifi_ssid: \"\"\nretronas_net_wifi_channel: 6\nretronas_net_wifi_hwmode: bg\nretronas_net_wifi_countrycode: AU\nretronas_net_wifi_ip: \"10.99.2.1\"\nretronas_net_wifi_subnet: \"23\"\nretronas_net_wifi_dhcprange: \"10.99.2.150,10.99.2.200,6h\"\nretronas_net_wifi_router: \"10.99.2.1\"\nretronas_net_wifi_ntp: \"10.99.2.1\"\nretronas_net_wifi_dns: \"10.99.1.1\"\nretronas_net_3dsqr_interface: \"eth0\"\n"
  },
  {
    "path": "ansible/roles/retronas.role.apt-backports/tasks/main.yaml",
    "content": "---\n- name: \"{{ my_name }} - Load RetroNAS config\"\n  ansible.builtin.include_vars: retronas_vars.yml\n\n- name: \"{{ my_name }} - install prerequisite packages\"\n  ansible.builtin.package:\n    name: \"{{ packages }}\"\n    state: latest\n\n- name: \"{{ my_name }} - discover signing key\"\n  ansible.builtin.shell:\n    cmd: curl -skL http://ftp.debian.org/debian/pool/main/d/debian-archive-keyring/ | grep -oP \"href=\\\"debian-archive-keyring.+.deb\\\">\" | sed -r 's/href=\\\"(debian-archive.+all.deb)\\\">/\\1/' | tail -n1\n  register: debian_signing_key\n  when: ansible_distribution == \"Debian\"\n\n- name: \"{{ my_name }} - download Debian signing keys\"\n  ansible.builtin.shell:\n    chdir: \"/tmp\"\n    cmd: \"aria2c --allow-overwrite=true http://ftp.debian.org/debian/pool/main/d/debian-archive-keyring/{{ debian_signing_key.stdout }} https://mirror.aarnet.edu.au/pub/debian/pool/main/d/debian-archive-keyring/{{ debian_signing_key.stdout }}\"\n  when: ansible_distribution == \"Debian\" and\n        debian_signing_key is defined\n\n- name: \"{{ my_name }} - install Debian signing keys\"\n  ansible.builtin.shell:\n    chdir: \"/tmp\"\n    cmd: \"dpkg -i {{ debian_signing_key.stdout }}\"\n  when: ansible_distribution == \"Debian\"\n\n- name: \"{{ my_name }} - Configure APT repo\"\n  ansible.builtin.apt_repository:\n    repo: deb http://deb.debian.org/debian {{ ansible_distribution_release }}-backports main contrib non-free\n    state: present\n    filename: debian-backports\n    update_cache: false\n  when: ansible_distribution == \"Debian\"\n\n- name: \"{{ my_name }} - Configure APT repo for Ubuntu x86_64\"\n  ansible.builtin.apt_repository:\n    repo: deb http://archive.ubuntu.com/ubuntu/ {{ ansible_distribution_release }}-backports main restricted universe multiverse\n    state: present\n    filename: \"{{ ansible_distribution_release }}-backports\"\n    update_cache: false\n  when: ansible_distribution == \"Ubuntu\" and\n        ansible_architecture == \"x86_64\"\n\n- name: \"{{ my_name }} - Configure APT repo for Ubuntu non x86_64 (ports)\"\n  ansible.builtin.apt_repository:\n    repo: deb http://ports.ubuntu.com/ {{ ansible_distribution_release }}-backports main restricted universe multiverse\n    state: present\n    filename: \"{{ ansible_distribution_release }}-backports\"\n    update_cache: false\n  when: ansible_distribution == \"Ubuntu\" and\n        ansible_architecture != \"x86_64\"\n\n- name: \"{{ my_name }} - Force update repo cache\"\n  ansible.builtin.shell:\n    cmd: \"/usr/bin/apt update || exit 0\"\n  when: ansible_distribution == \"Ubuntu\" or\n        ansible_distribution == \"Debian\"\n"
  },
  {
    "path": "ansible/roles/retronas.role.apt-backports/vars/main.yaml",
    "content": "---\nmy_name: \"Apt Backports\"\nmy_file: \"install_apt-backports\"\n\npackages:\n  - gnupg\n  - aria2\n  - curl\n"
  },
  {
    "path": "ansible/roles/retronas.role.cache/tasks/main.yaml",
    "content": "- name: \"{{ my_name }} - Gather required facts\"\n  ansible.builtin.setup:\n    gather_subset:\n      - date_time\n\n- name: \"{{ my_name }} - Check Ansible cache age\"\n  ansible.builtin.stat:\n    path: \"{{ retronas_cache_file }}\"\n  register: ansible_cache_age\n\n- name: \"{{ my_name }} - Check retronas_systems age\"\n  ansible.builtin.stat:\n    path: \"{{ retronas_systems_file }}\"\n  register: retronas_system_age\n\n- name: \"{{ my_name }} - Remove ansible cache if older than retronas_systems\"\n  ansible.builtin.file:\n    path: \"{{ retronas_cache_file }}\"\n    state: absent\n  become: yes\n  when: \n    - ansible_cache_age.stat.exists is true\n    - ansible_cache_age.stat.mtime < retronas_system_age.stat.mtime\n\n"
  },
  {
    "path": "ansible/roles/retronas.role.cache/vars/main.yaml",
    "content": "retronas_cache_file: \"{{ retronas_root }}/cache/s1_localhost\"\nretronas_systems_file: \"{{ retronas_root }}/ansible/retronas_systems.yml\"\n"
  },
  {
    "path": "ansible/roles/retronas.role.cockpit/handlers/main.yaml",
    "content": "- name: \"{{ my_name }} - Restart service\"\n  ansible.builtin.service:\n    name: \"{{ my_service }}\"\n    state: restarted\n    daemon_reload: yes"
  },
  {
    "path": "ansible/roles/retronas.role.cockpit/tasks/main.yaml",
    "content": "- name: \"{{ my_name }} - Load RetroNAS config\"\n  ansible.builtin.include_vars: retronas_vars.yml\n\n- name: \"{{ my_name }} - Install from backports repo\"\n  ansible.builtin.apt:\n    name: \"{{ packages }}\"\n    state: latest\n    default_release: \"{{ ansible_distribution_release }}-backports\"\n  notify: \"{{ my_name }} - Restart service\"\n  when: (ansible_distribution == 'Debian' and ansible_distribution_major_version | int < 13) or\n        ansible_distribution == 'Ubuntu'\n\n- name: \"{{ my_name }} - Install from repo\"\n  ansible.builtin.apt:\n    name: \"{{ packages }}\"\n    state: latest\n  notify: \"{{ my_name }} - Restart service\"\n  when: ansible_distribution == 'Debian' and\n        ansible_distribution_major_version | int >= 13\n\n- name: \"{{ my_name }} - enable startup services\"\n  ansible.builtin.service:\n    name: \"{{ my_service }}\"\n    state: started\n    enabled: true\n    daemon_reload: true\n\n#\n# FIREWALL\n#\n- name: \"{{ my_name }} - checking firewall rule\"\n  ansible.builtin.stat:\n    path: /etc/firewalld/services\n  register: firewalld_services\n\n- name: \"{{ my_name }} - enable firewall rule\"\n  ansible.posix.firewalld:\n    zone: \"{{ item.zone | default('retro') }}\"\n    service: \"{{ item.service | default('cockpit') }}\"\n    permanent: true\n    state: enabled\n    immediate: true\n  with_items: \"{{ firewalld_rules }}\"\n  when: firewalld_services.stat.exists\n"
  },
  {
    "path": "ansible/roles/retronas.role.cockpit/vars/main.yaml",
    "content": "my_name: \"Cockpit\"\nmy_file: \"install_cockpit\"\nmy_service: \"cockpit\"\n\npackages:\n  - cockpit\n  - cockpit-storaged\n  - cockpit-networkmanager\n\nfirewalld_rules:\n  - { zone: \"retro\" }\n  - { zone: \"modern\" }\n"
  },
  {
    "path": "ansible/roles/retronas.role.cockpit-packages/tasks/main.yaml",
    "content": "- name: \"{{ my_name }} - install prerequisite packages\"\n  ansible.builtin.package:\n      name: \"{{ package_tools }}\"\n      state: latest\n\n- name: \"{{ my_name }} - install cockpit packages inplace\"\n  ansible.builtin.copy:\n    src: \"templates/{{ my_file }}/{{ item }}.j2\"\n    dest: \"{{ retronas_root }}/scripts/{{ item }}\"\n    owner: root\n    group: root\n    mode: 0750\n  with_items:\n    \"{{ package_data }}\"\n\n- name: \"{{ my_name }} - get packages\"\n  ansible.builtin.command:\n    cmd: \"{{ retronas_root }}/scripts/{{ my_name }}.sh\"\n"
  },
  {
    "path": "ansible/roles/retronas.role.cockpit-packages/templates/install_cockpit-packages/cockpit-packages.sh.j2",
    "content": "#!/bin/bash\n#\n# Clone down additional Cockpit Packages\n#\n\nset -u\n\nRNTMP=/tmp/rn_cpack\nCOCKPIT=/usr/share/cockpit\n\n# clean up old stuff\n[ -d \"${RNTMP}\" ] && rm -Rf \"${RNTMP}/\"\n\n# make out temp dir\n[ ! -d ${RNTMP} ] && mkdir ${RNTMP}\n\nfunction get_github {\n\n    local ACCOUNT=$1\n    local REPO=$2\n    local FOLDER=$3\n\n    if [ ! -d \"${RNTMP}/${FOLDER}\" ]\n    then\n        cd ${RNTMP}\n        git clone https://github.com/${ACCOUNT}/${REPO}.git $FOLDER\n    else\n        cd ${RNTMP}/${FOLDER}\n        git pull\n    fi\n\n    cd ${RNTMP}/${FOLDER}\n\n    RELEASE=$(git tag | tail -n1)\n    [ ! -z \"${RELEASE}\" ] && git checkout $RELEASE\n\n}\n\nfunction install_package {\n\n    local FOLDER=$1\n\n    [ ! -d \"${RNTMP}/${FOLDER}\" ] && echo \"Can't find my temp dir man, boooo!\" && exit 1\n    \n    cp -R \"${RNTMP}/${FOLDER}\" \"${COCKPIT}/\"\n\n}\n\n\n### there'll be a better way to approach this\nfunction get_45drives {\n\n    PACKAGES=(\n        \"cockpit-navigator|navigator\"\n        #\"cockpit-zfs-manager|zfs\"\n        #\"cockpit-file-sharing|file-sharing\"\n    )\n\n    for ITEM in ${PACKAGES[@]}\n    do\n        PACKAGE=$(echo ${ITEM} | cut -d '|' -f1)\n        FOLDER=$(echo ${ITEM} | cut -d'|' -f2)\n        get_github 45Drives $PACKAGE $FOLDER\n        install_package $FOLDER/$FOLDER\n    done\n\n}\n\n### get sensors\nfunction get_sensors {\n\n    PACKAGE=cockpit-sensors-plugin\n    FOLDER=$(echo ${PACKAGE} | sed -r 's/^cockpit-(.+)-plugin/\\1/' )\n    get_github JamesGKent $PACKAGE $FOLDER\n    install_package $FOLDER\n\n}\n\n\nget_45drives\nget_sensors\n\n# clean up\n[ -d \"${RNTMP}\" ] && rm -Rf \"${RNTMP}/\"\n"
  },
  {
    "path": "ansible/roles/retronas.role.cockpit-packages/vars/main.yaml",
    "content": "my_name: \"cockpit-packages\"\nmy_file: \"install_cockpit-packages\"\n\npackage_tools:\n  - lm-sensors\n\npackage_data:\n  - cockpit-packages.sh"
  },
  {
    "path": "ansible/roles/retronas.role.createdirs/tasks/main.yaml",
    "content": "---\n- name: \"{{ my_name }} - Include systems map\"\n  ansible.builtin.include_vars: \"retronas_systems.yml\"\n\n- name: \"{{ my_name }} - Load RetroNAS config\"\n  ansible.builtin.include_vars: retronas_vars.yml\n\n- name: \"{{ my_name }} - stat our path\"\n  ansible.builtin.stat:\n    path: \"{{ retronas_path }}\"\n  register: rn_path\n\n- name: \"{{ my_name }} - create our base\"\n  ansible.builtin.shell:\n    cmd: \"mkdir -p {{ rn_path }}\"\n  changed_when: false\n  when: rn_path is defined and\n        rn_path.stat.exists is false\n\n- name: \"{{ my_name }} - perms\"\n  ansible.builtin.shell:\n    cmd: \"chown -R {{ retronas_user }}:{{ retronas_group }} {{ rn_path }}\"\n  changed_when: false\n  when: rn_path is defined and\n        rn_path.stat.exists is false\n\n- name: \"{{ my_name }} - stat top level paths\"\n  ansible.builtin.stat:\n    path: \"{{ retronas_path }}/{{ item }}\"\n  loop: [ \"roms\", \"bios\", \"saves\", \"savestates\" ]\n  register: tl_paths\n\n- name: \"{{ my_name }} - create our missing paths\"\n  ansible.builtin.file:\n    path: \"{{ retronas_path }}/{{ item.item }}\"\n    owner: \"{{ retronas_user }}\"\n    group: \"{{ retronas_group }}\"\n    state: directory\n    mode: '0755'\n  loop: \"{{ tl_paths.results }}\"\n  when: tl_paths is defined and\n        item.stat.exists is false\n\n- name: \"{{ my_name }} - build layout list (set fact)\"\n  ansible.builtin.set_fact:\n    path_list: \"{{ path_list|default([]) + [ retronas_path + '/' + pl.1.name|lower + '/' + pl.0.src if pl.1.systems is true else '' ] }}\"\n  loop: \"{{ system_map|product(top_level_paths)|list }}\"\n  loop_control:\n    loop_var: pl\n    label: \"{{ pl.0.src }}\"\n  when: pl.1.enabled is true\n\n- name: \"{{ my_name }} - build layout\"\n  ansible.builtin.shell:\n    cmd: mkdir -p {{ path_list|flatten|join(' ') }}\n  become: true\n  become_user: \"{{ retronas_user }}\"\n  when: path_list is defined\n  changed_when: false\n\n- name: \"{{ my_name }} - build systems directory layout (set fact)\"\n  ansible.builtin.set_fact:\n    link_list: \"{{ link_list|default([]) + [ 'ln -sfT ' + retronas_path  + '/' + ll.1.name|lower + '/' + ll.0.src + ' ' + retronas_path  + '/' +  ll.1.name + '/' + ll.0.dest + ';' ] }}\"\n  loop: \"{{ system_links|product(top_level_paths)|list }}\"\n  loop_control:\n    loop_var: ll\n    label: \"{{ ll.0.src }}\"\n  when:\n    - ll.1.enabled is true\n    - ll.1.systems is true\n\n- name: \"{{ my_name }} - build systems directory layout (createdirs)\"\n  ansible.builtin.shell:\n    cmd: \"{{ link_list|join('') }}\"\n  when: link_list is defined\n  changed_when: false\n\n- name: \"{{ my_name }} - add top level directory info\"\n  ansible.builtin.copy:\n    dest: \"{{ item.stat.path }}/dir.txt\"\n    content: |\n      this folder structure is the responsiblity of the user to populate\n    owner: \"{{ retronas_user }}\"\n    group: \"{{ retronas_group }}\"\n    mode: \"0644\"\n  loop: \"{{ tl_paths.results }}\"\n  when: tl_paths is defined and\n        item.stat.exists is true\n"
  },
  {
    "path": "ansible/roles/retronas.role.curlftpfs/tasks/main.yaml",
    "content": "- name: \"{{ my_name }} - Load RetroNAS config\"\n  ansible.builtin.include_vars: retronas_vars.yml\n\n- ansible.builtin.import_role:\n    name: retronas.role.package.latest\n\n- name: \"{{ my_name }} - templates\"\n  ansible.builtin.template:\n    src: \"{{ item.name }}.j2\"\n    dest: \"{{ item.dest }}/{{ item.name }}\"\n    owner: \"{{ item.owner|default('root') }}\"\n    group: \"{{ item.group|default('root') }}\"\n    mode: \"{{ item.mode|default('0644') }}\"\n    force: \"{{ item.force|default('yes') }}\"\n  with_items: \"{{ templates }}\"\n\n- name: \"{{ my_name }} - Install from source code\"\n  ansible.builtin.shell: \"{{ retronas_root }}/scripts/{{ my_file }}.sh 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n  args:\n    creates: \"/usr/local/bin/curlftpfs\"\n"
  },
  {
    "path": "ansible/roles/retronas.role.curlftpfs/templates/install_curlftpfs.sh.j2",
    "content": "# curlftpfs\n\nset -u\n\nVERS=${1:-0.9.2}\nAPPNAME=curlftpfs-${VERS}\nREPO=https://ixpeering.dl.sourceforge.net/project/curlftpfs/curlftpfs/${VERS}/${APPNAME}.tar.gz?viasf=1\nBUILDPATH={{ retronas_root }}/src\n\n\n[ ! -d ${BUILDPATH} ] && mkdir -p ${BUILDPATH}\n\ncd ${BUILDPATH}\nif $(curl -O $REPO) \nthen\n    tar xvf ${APPNAME}.tar.gz\n    cd ${APPNAME}\n    ./configure\n    make\n    make install\nfi\n\nrm -rf ${BUILDPATH}\n"
  },
  {
    "path": "ansible/roles/retronas.role.curlftpfs/vars/main.yaml",
    "content": "my_name: \"curlftpfs\"\nmy_file: \"install_{{ my_name }}\"\n\npackages:\n  - curl\n  - build-essential\n  - libglib2.0-dev\n  - libfuse-dev\n  - libcurl4-openssl-dev\n\ntemplates:\n  - { name: \"{{ my_file }}.sh\", dest: \"{{ retronas_root }}/scripts\", mode: \"0755\"}\n"
  },
  {
    "path": "ansible/roles/retronas.role.dotnetcore3/tasks/main.yaml",
    "content": "- name: \"{{ my_name }} - Load RetroNAS config\"\n  ansible.builtin.include_vars: retronas_vars.yml\n\n- ansible.builtin.import_role:\n    name: retronas.role.package.latest\n\n- name: \"{{ my_name }} - clean up old installs\"\n  ansible.builtin.file:\n    path: \"{{ srcdir }}\"\n    force: yes\n    state: absent\n\n- name: \"{{ my_name }} - create build dirs\"\n  ansible.builtin.file:\n    path: \"{{ srcdir }}\"\n    owner: root\n    group: root\n    mode: 0755\n    state: directory\n\n- name: \"{{ my_name }} - Download SDK installer\"\n  ansible.builtin.get_url:\n    url: https://dotnet.microsoft.com/download/dotnet/scripts/v1/dotnet-install.sh\n    dest: \"{{ srcdir }}\"\n    owner: root\n    group: root\n    mode: 0755\n\n- name: \"{{ my_name }} - Install DotNet SDK\"\n  ansible.builtin.shell:\n    cmd: \"./dotnet-install.sh --version {{ version }} 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n    chdir: \"{{ srcdir }}\"\n    executable: /bin/bash\n  environment:\n    DOTNET_ROOT: \"{{ retronas_root }}/bin/dotnetcore3\"\n    DOTNET_INSTALL_DIR: \"{{ retronas_root }}/bin/dotnetcore3\"\n    TMPDIR: \"{{ srcdir }}\""
  },
  {
    "path": "ansible/roles/retronas.role.dotnetcore3/vars/main.yaml",
    "content": "my_name: \"DotNet Core 3.X SDK\"\nmy_file: \"install_dotnetcore3\"\n\npackages:\n  - wget\n  - curl\n  - aria2\n  - coreutils\n\nsrcdir: \"{{ retronas_root }}/src/{{ my_file }}\"\nversion: 3.1.415\narch: x64\n"
  },
  {
    "path": "ansible/roles/retronas.role.dotnetcore6/tasks/main.yaml",
    "content": "- name: \"{{ my_name }} - Load RetroNAS config\"\n  ansible.builtin.include_vars: retronas_vars.yml\n\n- ansible.builtin.import_role:\n    name: retronas.role.package.latest\n\n- name: \"{{ my_name }} - clean up old installs\"\n  ansible.builtin.file:\n    path: \"{{ srcdir }}\"\n    force: yes\n    state: absent\n\n- name: \"{{ my_name }} - create build dirs\"\n  ansible.builtin.file:\n    path: \"{{ srcdir }}\"\n    owner: root\n    group: root\n    mode: 0755\n    state: directory\n\n- name: \"{{ my_name }} - Download SDK installer\"\n  ansible.builtin.get_url:\n    url: https://dotnet.microsoft.com/download/dotnet/scripts/v1/dotnet-install.sh\n    dest: \"{{ srcdir }}\"\n    owner: root\n    group: root\n    mode: 0755\n\n- name: \"{{ my_name }} - Install DotNet SDK\"\n  ansible.builtin.shell:\n    cmd: \"./dotnet-install.sh --version {{ version }} 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n    chdir: \"{{ srcdir }}\"\n    executable: /bin/bash\n  environment:\n    DOTNET_ROOT: \"{{ retronas_root }}/bin/dotnetcore6\"\n    DOTNET_INSTALL_DIR: \"{{ retronas_root }}/bin/dotnetcore6\"\n    TMPDIR: \"{{ srcdir }}\""
  },
  {
    "path": "ansible/roles/retronas.role.dotnetcore6/vars/main.yaml",
    "content": "my_name: \"DotNet Core 6.X SDK\"\nmy_file: \"install_dotnetcore6\"\n\npackages:\n- wget\n- curl\n- aria2\n- coreutils\n\nsrcdir: \"{{ retronas_root }}/src/{{ my_file }}\"\nversion: 6.0.400\narch: x64"
  },
  {
    "path": "ansible/roles/retronas.role.dotnetcore8/tasks/main.yaml",
    "content": "- name: \"{{ my_name }} - Load RetroNAS config\"\n  ansible.builtin.include_vars: retronas_vars.yml\n\n- ansible.builtin.import_role:\n    name: retronas.role.package.latest\n\n- name: \"{{ my_name }} - clean up old installs\"\n  ansible.builtin.file:\n    path: \"{{ srcdir }}\"\n    force: yes\n    state: absent\n\n- name: \"{{ my_name }} - create build dirs\"\n  ansible.builtin.file:\n    path: \"{{ srcdir }}\"\n    owner: root\n    group: root\n    mode: 0755\n    state: directory\n\n- name: \"{{ my_name }} - Download SDK installer\"\n  ansible.builtin.get_url:\n    url: https://dotnet.microsoft.com/download/dotnet/scripts/v1/dotnet-install.sh\n    dest: \"{{ srcdir }}\"\n    owner: root\n    group: root\n    mode: 0755\n\n- name: \"{{ my_name }} - Install DotNet SDK\"\n  ansible.builtin.shell:\n    cmd: \"./dotnet-install.sh --version {{ version }} 2>&1 | tee {{ retronas_root }}/log/{{ my_file }}.log\"\n    chdir: \"{{ srcdir }}\"\n    executable: /bin/bash\n  environment:\n    DOTNET_ROOT: \"{{ retronas_root }}/bin/dotnetcore8\"\n    DOTNET_INSTALL_DIR: \"{{ retronas_root }}/bin/dotnetcore8\"\n    TMPDIR: \"{{ srcdir }}\"\n"
  },
  {
    "path": "ansible/roles/retronas.role.dotnetcore8/vars/main.yaml",
    "content": "my_name: \"DotNet Core 8.X SDK\"\nmy_file: \"install_dotnetcore8\"\n\npackages:\n- wget\n- curl\n- aria2\n- coreutils\n\nsrcdir: \"{{ retronas_root }}/src/{{ my_file }}\"\nversion: 8.0.416\narch: x64\n"
  },
  {
    "path": "ansible/roles/retronas.role.extradirs/tasks/main.yaml",
    "content": "- ansible.builtin.import_role:\n    name: retronas.role.createdirs"
  },
  {
    "path": "ansible/roles/retronas.role.extradirs/vars/main.yaml",
    "content": "my_name: \"Generic Extra dir(s)\"\nmy_file: \"install_romdir\"\nmodule_name: \"extradirs\"\n\ntop_level_paths:\n  - { name: \"saves\",      enabled: yes, systems: yes }\n  - { name: \"savestates\", enabled: yes, systems: yes }\n  - { name: \"bios\",       enabled: yes, systems: yes }\n  - { name: \"wallpapers\", enabled: yes, systems: no }"
  },
  {
    "path": "ansible/roles/retronas.role.filesystems/tasks/main.yaml",
    "content": "- name: \"{{ my_name }} - Load RetroNAS config\"\n  ansible.builtin.include_vars: retronas_vars.yml\n    \n- ansible.builtin.import_role:\n    name: retronas.role.package.latest"
  },
  {
    "path": "ansible/roles/retronas.role.filesystems/vars/main.yaml",
    "content": "my_name: \"RetroNAS filesystems\"\nmy_file: \"install_filesystems\"\n\npackages:\n  - btrfs-progs\n  - ntfs-3g\n  - exfatprogs\n  - exfat-fuse\n  - hfsplus\n  - xfsprogs\n  - nfs-common\n  - e2fsprogs\n  - dosfstools\n  #- curlftpfs\n  - sshfs\n  - udisks2\n  - udisks2-btrfs\n  - udisks2-lvm2\n  #- udisks2-zram\n"
  },
  {
    "path": "ansible/roles/retronas.role.firewalld.port/tasks/main.yaml",
    "content": "#\n# FIREWALL\n#\n- name: \"{{ my_name }} - checking firewall rule\"\n  ansible.builtin.stat:\n    path: /etc/firewalld/services\n  register: firewalld_services\n\n- name: \"{{ my_name }} - reload to pick up any new config\"\n  ansible.builtin.service:\n    name: \"firewalld\"\n    state: reloaded\n  when: firewalld_services.stat.exists\n\n- name: \"{{ my_name }} - enable firewall rule (port)\"\n  ansible.posix.firewalld:\n    zone: \"{{ item.zone | default('retro') }}\"\n    port: \"{{ item.port }}/{{ item.protocol }}\"\n    permanent: \"{{ item.permanent | default('true') }}\"\n    state: \"{{ item.state | default('enabled') }}\"\n    immediate: true\n  loop: \"{{ firewalld_ports }}\"\n  when: firewalld_ports is defined and\n        firewalld_services.stat.exists\n\n- name: \"{{ my_name }} - enable firewall rule (service)\"\n  ansible.posix.firewalld:\n    zone: \"{{ item.zone | default('retro') }}\"\n    service: \"{{ item.service | default('ssh') }}\"\n    permanent: true\n    state: enabled\n    immediate: true\n  with_items: \"{{ firewalld_rules }}\"\n  when: firewalld_rules is defined and\n        firewalld_services.stat.exists"
  },
  {
    "path": "ansible/roles/retronas.role.htpasswd/tasks/main.yaml",
    "content": "---\n- name: \"{{ my_name }} - Load RetroNAS config\"\n  ansible.builtin.include_vars: retronas_vars.yml\n\n- name: \"{{ my_name }} - manage htpasswd\"\n  ansible.builtin.file: \n    path: \"{{ retronas_htpasswd }}\"\n    owner: \"{{ retronas_user }}\"\n    group: \"{{ retronas_group }}\"\n    state: touch\n    mode: \"0640\"\n"
  },
  {
    "path": "ansible/roles/retronas.role.htpasswd/vars/main.yml",
    "content": "---\nretronas_htpasswd: \"/etc/retronas.htpasswd\"\n"
  },
  {
    "path": "ansible/roles/retronas.role.nfs/handlers/main.yaml",
    "content": "- name: \"{{ my_name }} - Restart service\"\n  ansible.builtin.service:\n    name: \"{{ item }}\"\n    state: restarted\n  with_items:\n    - nfs-kernel-server"
  },
  {
    "path": "ansible/roles/retronas.role.nfs/tasks/main.yaml",
    "content": "- name: \"{{ my_name }} - Load RetroNAS config\"\n  ansible.builtin.include_vars: retronas_vars.yml\n\n- ansible.builtin.import_role:\n    name: retronas.role.package.latest\n\n- name: \"{{ my_name }} - retrieve {{ retronas_user }} user info\"\n  ansible.builtin.getent:\n    database: passwd\n    key: \"{{ retronas_user }}\"\n\n- ansible.builtin.set_fact:\n    retronas_user_id: \"{{ getent_passwd[retronas_user][1] }}\"\n\n- name: \"{{ my_name }} - retrieve {{ retronas_group }} group info\"\n  ansible.builtin.getent:\n    database: group\n    key: \"{{ retronas_group }}\"\n\n- ansible.builtin.set_fact:\n    retronas_group_id: \"{{ getent_group[retronas_group][1] }}\"\n\n- ansible.builtin.import_role:\n    name: retronas.role.templates\n\n- name: \"{{ my_name }} - enable startup services\"\n  ansible.builtin.service:\n    name: \"{{ item }}\"\n    state: started\n    enabled: yes\n  with_items:\n    - nfs-kernel-server\n\n- ansible.builtin.import_role:\n    name: retronas.role.firewalld.port"
  },
  {
    "path": "ansible/roles/retronas.role.nfs/vars/main.yaml",
    "content": "my_name: \"NFS\"\nmy_file: \"install_nfs\"\nmodule_name: \"nfs\"\n\npackages:\n  - nfs-common\n  - nfs-kernel-server\n\npaths:\n  - { name: \"exports.d\", dest: \"/etc\" }\n\ntemplates:\n  - { name: \"nfs-kernel-server\", dest: \"/etc/default\" }\n  - { name: \"exports\", dest: \"/etc/\" }\n\nfirewalld_rules:\n  - { zone: retro, service: nfs }\n  - { zone: modern, service: nfs }"
  },
  {
    "path": "ansible/roles/retronas.role.nginx/handlers/main.yaml",
    "content": "- name: \"restart nginx\"\n  ansible.builtin.service:\n    name: \"{{ item.name }}\"\n    state: restarted\n  with_items: \"{{ systemd_units }}\"\n  when: item.restart == \"yes\""
  },
  {
    "path": "ansible/roles/retronas.role.nginx/tasks/main.yaml",
    "content": "---\n- name: \"{{ my_name }} - Load RetroNAS config\"\n  ansible.builtin.include_vars: retronas_vars.yml\n\n- ansible.builtin.import_role:\n    name: retronas.role.sslcert\n\n- name: \"{{ my_name }} - manage packages\"\n  ansible.builtin.package:\n    name: \"{{ item.name }}\"\n    state: \"{{ item.state }}\"\n  with_items: \"{{ packages }}\"\n  notify: \"restart nginx\"\n\n- ansible.builtin.import_role:\n    name: retronas.role.paths\n\n- name: \"{{ my_name }} - remove old config\"\n  ansible.builtin.file:\n    path: \"{{ item }}/99-retronas.conf\"\n    state: absent\n  with_items:\n    - \"{{ nginx_sites_available }}\"\n    - \"{{ nginx_sites_enabled }}\"\n\n- name: \"{{ my_name }} - config dirs\"\n  ansible.builtin.file:\n    path: \"/etc/{{ my_name }}/{{ item.path }}\"\n    owner: \"{{ item.owner|default('root') }}\"\n    group: \"{{ item.group|default('root') }}\"\n    mode: \"{{ item.mode|default('0750') }}\"\n    state: directory\n  loop: \"{{ lookup('filetree', 'templates/' + my_file + '/conf', wantlist=True) }}\"\n  when: item.state == 'directory'\n  notify: \"restart nginx\"\n\n- name: \"{{ my_name }} - config files\"\n  ansible.builtin.template:\n    src: \"{{ item.src }}\"\n    dest: \"/etc/{{ my_name }}/{{ item.path }}\"\n    owner: \"{{ item.owner|default('root') }}\"\n    group: \"{{ item.group|default('root') }}\"\n    mode: \"{{ item.mode|default('0644') }}\"\n    force: \"{{ item.force|default('yes') }}\"\n  loop: \"{{ lookup('filetree', 'templates/' + my_file + '/conf', wantlist=True) }}\"\n  when: item.state == 'file'\n  notify: \"restart nginx\"\n\n- name: \"{{ my_name }} - www dirs\"\n  ansible.builtin.file:\n    path: \"{{ retronas_path }}/www/{{ item.path }}\"\n    owner: \"{{ retronas_user }}\"\n    group: \"{{ retronas_group }}\"\n    mode: \"{{ item.mode|default('0750') }}\"\n    state: directory\n  loop: \"{{ lookup('filetree', 'templates/' + my_file +' /www', wantlist=True) }}\"\n  when: item.state == 'directory'\n  notify: \"restart nginx\"\n\n- name: \"{{ my_name }} - www files\"\n  ansible.builtin.template:\n    src: \"{{ item.src }}\"\n    dest: \"{{ retronas_path }}/www/{{ item.path }}\"\n    owner: \"{{ retronas_user }}\"\n    group: \"{{ retronas_group }}\"\n    mode: \"{{ item.mode|default('0644') }}\"\n    force: \"{{ item.force|default('yes') }}\"\n  loop: \"{{ lookup('filetree', 'templates/' + my_file + '/www', wantlist=True) }}\"\n  when: item.state == 'file'\n  notify: \"restart nginx\"\n\n- name: \"{{ my_name }} - enable sites\"\n  ansible.builtin.file:\n    src: \"{{ nginx_sites_available }}/{{ item.name }}\"\n    dest: \"{{ nginx_sites_enabled }}/{{ item.name }}\"\n    state: \"link\"\n  with_items: \"{{ templates }}\"\n  when: item.enable is defined and\n        item.enable == \"yes\"\n  notify: \"restart nginx\"\n\n- name: \"{{ my_name }} - enable this site\"\n  ansible.builtin.file:\n    src: \"{{ nginx_sites_available }}/{{ site_enable }}\"\n    dest: \"{{ nginx_sites_enabled }}/{{ site_enable }}\"\n    state: \"link\"\n  when: site_enable | length > 0\n  notify: \"restart nginx\"\n\n- name: \"{{ my_name }} - disable sites\"\n  ansible.builtin.file:\n    path: \"{{ nginx_sites_enabled }}/{{ item }}\"\n    state: \"absent\"\n  with_items: \"{{ sites_disable }}\"\n  notify: \"restart nginx\"\n\n- name: \"{{ my_name }} - update runas user\"\n  ansible.builtin.lineinfile:\n    path: /etc/{{ my_name }}/{{ my_name }}.conf\n    regexp: '^user\\s[a-z-]+;$'\n    line: user {{ retronas_user }};\n    state: present\n  notify: \"restart nginx\"\n\n- name: \"{{ my_name }} - enable startup services\"\n  ansible.builtin.service:\n    name: \"{{ item.name }}\"\n    state: \"{{ item.state }}\"\n    enabled: \"{{ item.enabled }}\"\n  with_items: \"{{ systemd_units }}\"\n\n#\n# FIREWALL\n#\n- name: \"{{ my_name }} - checking firewall rule\"\n  ansible.builtin.stat:\n    path: /etc/firewalld/services\n  register: firewalld_services\n\n- name: \"{{ my_name }} - enable firewall rule\"\n  ansible.posix.firewalld:\n    zone: retro\n    service: \"{{ item }}\"\n    permanent: true\n    state: enabled\n    immediate: true\n  with_items:\n    - http\n    - https\n  when: firewalld_services.stat.exists\n"
  },
  {
    "path": "ansible/roles/retronas.role.nginx/templates/install_nginx/conf/includes/autoindex.conf",
    "content": "autoindex on;\nautoindex_exact_size off;\nautoindex_format html;\nautoindex_localtime on;"
  },
  {
    "path": "ansible/roles/retronas.role.nginx/templates/install_nginx/conf/includes/listen-80.conf",
    "content": "listen 80;\nlisten [::]:80;"
  },
  {
    "path": "ansible/roles/retronas.role.nginx/templates/install_nginx/conf/includes/ssl.conf",
    "content": "listen 443 ssl;\nlisten [::]:443 ssl;\n\nssl_certificate     /etc/ssl/private/retronas.crt;\nssl_certificate_key /etc/ssl/private/retronas.key;\nssl_protocols       TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;\nssl_ciphers         HIGH:!aNULL:!MD5;"
  },
  {
    "path": "ansible/roles/retronas.role.nginx/templates/install_nginx/conf/sites-available/10-retronas.conf",
    "content": "server {\n    server_name retronas;\n    server_name retro;\n    server_name www.*;\n\n    include includes/listen-80.conf;\n    include includes/ssl.conf;\n\n    location / { \n        root {{ retronas_path }}/www; \n    }\n\n    location /files { \n        alias {{ retronas_path }}; \n        index index.html; \n        include includes/autoindex.conf;\n    }\n\n    location /3ds { \n        alias {{ retronas_path }}/3ds; \n        index index.html; \n        include includes/autoindex.conf;\n    }\n}\n"
  },
  {
    "path": "ansible/roles/retronas.role.nginx/templates/install_nginx/conf/sites-available/99-retronas-files.conf",
    "content": "server {\n    server_name files.*;\n\n    include includes/listen-80.conf;\n    include includes/ssl.conf;\n\n    location / { \n        root {{ retronas_path }};\n        index index.html;\n        include includes/autoindex.conf;\n    }\n}\n"
  },
  {
    "path": "ansible/roles/retronas.role.nginx/templates/install_nginx/www/index.html",
    "content": "<html lang=\"en\">\n<head>\n<title>retroNAS</title>\n</head>\n<body>\n        <a href=\"//files.retro\">files</a><br />\n        <a href=\"//nas.retro\">nas</a><br />\n        <a href=\"//fenrir.retro\">fenrir</a><br />\n        <a href=\"//cockpit.retro\">cockpit</a><br />\n        <a href=\"//deluge.retro\">deluge</a><br />\n        <a href=\"//syncthing.retro\">syncthing</a><br />\n        <a href=\"//xlink.retro\">xlink</a><br />\n</body>"
  },
  {
    "path": "ansible/roles/retronas.role.nginx/vars/main.yaml",
    "content": "my_name: \"nginx\"\nmy_file: \"install_{{ my_name }}\"\n\nnginx_sites_available: \"/etc/{{ my_name }}/sites-available\"\nnginx_sites_enabled: \"/etc/{{ my_name }}/sites-enabled\"\n\npaths:\n- { name: \"www\", dest: \"/{{ retronas_path }}\" }\n\npackages:\n- { name: \"lighttpd\", state: \"absent\" }\n- { name: \"{{ my_name }}\", state: \"latest\" }\n\ntemplates:\n- { name: \"10-retronas.conf\",         dest: \"{{ nginx_sites_available }}\",  mode: \"0640\", enable: \"yes\" }\n- { name: \"99-retronas-files.conf\",   dest: \"{{ nginx_sites_available }}\",  mode: \"0640\", enable: \"yes\" }\n\nsites_disable:\n- \"default\"\n\nsystemd_units:\n- { name: \"{{ my_name }}\", type: 'service', state: \"started\", enabled: \"yes\", restart: \"yes\", instance: \"no\" }\n\nsite_enable: \"\""
  },
  {
    "path": "ansible/roles/retronas.role.package.latest/tasks/main.yaml",
    "content": "- name: \"{{ my_name }} - Install packages\"\n  ansible.builtin.package:\n    name: \"{{ packages }}\"\n    state: latest\n    update_cache: true\n  become: true\n"
  },
  {
    "path": "ansible/roles/retronas.role.paths/tasks/main.yaml",
    "content": "- name: \"{{ my_name }} - paths\"\n  ansible.builtin.file:\n    src: \"{{ item.src | default(omit) }}\"\n    dest: \"{{ item.dest + '/' | default(omit) }}{{ item.name }}\"\n    owner: \"{{ item.owner|default('root') }}\"\n    group: \"{{ item.group|default('root') }}\"\n    mode: \"{{ item.mode|default('0755') }}\"\n    state: \"{{ item.state|default('directory') }}\"\n  loop: \"{{ paths }}\"\n  when: paths is defined"
  },
  {
    "path": "ansible/roles/retronas.role.romdir/tasks/main.yaml",
    "content": "---\n- ansible.builtin.import_role:\n    name: retronas.role.createdirs\n"
  },
  {
    "path": "ansible/roles/retronas.role.romdir/vars/main.yaml",
    "content": "---\nmy_name: \"Generic ROM dir\"\nmy_file: \"install_romdir\"\nmodule_name: \"romdir\"\n\ntop_level_paths:\n  - { name: \"roms\", enabled: yes, systems: yes }\n"
  },
  {
    "path": "ansible/roles/retronas.role.samba/handlers/main.yaml",
    "content": "- name: \"restart samba\"\n  ansible.builtin.service:\n    name: \"{{ item }}\"\n    state: restarted\n  loop: \"{{ my_services }}\"\n"
  },
  {
    "path": "ansible/roles/retronas.role.samba/tasks/main.yaml",
    "content": "---\n- name: \"{{ my_name }} - Load RetroNAS config\"\n  ansible.builtin.include_vars: retronas_vars.yml\n\n- ansible.builtin.import_role:\n    name: retronas.role.package.latest\n\n- ansible.builtin.import_role:\n    name: retronas.role.templates\n\n- name: \"{{ my_name }} - configure samba config\"\n  ansible.builtin.ini_file:\n    path: /etc/samba/smb.conf\n    section: \"{{ item.section }}\"\n    option: \"{{ item.option }}\"\n    value: \"{{ item.value }}\"\n  loop: \"{{ config_opts }}\"\n\n- name: \"{{ my_name }} - restarted/enabled\"\n  ansible.builtin.service:\n    name: \"{{ item }}\"\n    state: restarted\n    enabled: true\n  loop: \"{{ my_services }}\"\n\n- ansible.builtin.import_role:\n    name: retronas.role.firewalld.port\n"
  },
  {
    "path": "ansible/roles/retronas.role.samba/templates/retronas.conf.j2",
    "content": "## RetroNAS config.\n## This is autogenerated. Changes will be lost.\ncomment = retronas\npath = {{ retronas_path }}\nguest ok = no\nbrowseable = yes\nwrite list = {{ retronas_user }}\nwriteable = yes\nforce user = {{ retronas_user }}\nforce group = {{ retronas_group }}\ncreate mask = 0775\ndirectory mask = 0775\nfollow symlinks = yes\nwide links = yes"
  },
  {
    "path": "ansible/roles/retronas.role.samba/vars/main.yaml",
    "content": "---\nmy_name: \"Samba\"\nmy_file: \"\"  # required to clear out any play set var\n\npackages:\n  - avahi-daemon\n  - samba\n  - samba-vfs-modules\n\nmy_services:\n  - avahi-daemon\n  - smbd\n  - nmbd\n\nconfig_opts:\n  - { section: \"global\",    option: \"min protocol\",               value: \"CORE\"}\n  - { section: \"global\",    option: \"netbios name\",               value: \"retrosmb\"}\n  - { section: \"global\",    option: \"lanman auth\",                value: \"yes\"}\n  - { section: \"global\",    option: \"client lanman auth\",         value: \"yes\"}\n  - { section: \"global\",    option: \"ntlm auth\",                  value: \"yes\"}\n  - { section: \"global\",    option: \"client ntlm auth\",           value: \"yes\"}\n  - { section: \"global\",    option: \"ntlmv2 auth\",                value: \"yes\"}\n  - { section: \"global\",    option: \"client ntlmv2 auth\",         value: \"yes\"}\n  - { section: \"global\",    option: \"unix extensions\",            value: \"no\"}\n  - { section: \"global\",    option: \"allow insecure wide links\",  value: \"yes\"}\n  - { section: \"global\",    option: \"socket options\",             value: \"TCP_NODELAY IPTOS_LOWDELAY SO_KEEPALIVE\"}\n  - { section: \"global\",    option: \"strict locking\",             value: \"no\"}\n  - { section: \"global\",    option: \"strict sync\",                value: \"no\"}\n  - { section: \"global\",    option: \"sync always\",                value: \"no\"}\n  - { section: \"global\",    option: \"deadtime\",                   value: \"1560\"}\n  - { section: \"retronas\",  option: \"include\",                    value: \"/etc/samba/retronas.conf\"}\n\ntemplates:\n  - { name: \"retronas.conf\", dest: \"/etc/samba\" }\n\nfirewalld_rules:\n  - { zone: \"retro\",  service: \"samba\" }\n  - { zone: \"modern\", service: \"samba-modern\" }\n\nchanged: false\n"
  },
  {
    "path": "ansible/roles/retronas.role.samba.system/tasks/link.yml",
    "content": "---\n- name: \"{{ my_name }} - build layout list (set fact) (links)\"\n  ansible.builtin.set_fact:\n    path_list_ll: \"{{ path_list_ll|default([]) + [ 'ln -sfT ../roms/' + item.src + ' \\\"' + retronas_path + '/' + system_key + '/' + item[system_key] + '\\\";' ] }}\"\n  loop: \"{{ system_map }}\"\n  loop_control:\n    label: \"{{ item.src }}\"\n  when: top_level_paths is not defined and\n    item[system_key] is defined and\n    item[system_key] | length > 0\n\n- name: \"{{ my_name }} - build internal directory layout (links)\"\n  ansible.builtin.file:\n    src: \"../roms/{{ item.src }}\"\n    dest: \"{{ retronas_path }}/{{ system_key }}/{{ item.dest }}\"\n    owner: \"{{ retronas_user }}\"\n    group: \"{{ retronas_group }}\"\n    state: link\n  loop: \"{{ internal_symlinks }}\"\n  loop_control:\n    label: \"{{ item.src }}\"\n  when: internal_symlinks is defined and\n    top_level_paths is not defined\n\n- name: \"{{ my_name }} - build layout list (set fact) (top level paths) (links)\"\n  ansible.builtin.set_fact:\n    path_list_tl: \"{{ path_list_tl|default([]) + [ 'ln -sfT ../../' + item.1.generic|lower + '/' + item.0.src + ' \\\"' + retronas_path + '/' + system_key + '/' + item.1.name + '/' + item.0[system_key] + '\\\";' ] }}\"\n  loop: \"{{ system_map|product(top_level_paths)|list }}\"\n  loop_control:\n    label: \"{{ item.0.src }}\"\n  when: top_level_paths is defined and\n    item.1.enabled is true and\n    item.0[system_key] is defined and\n    item.0[system_key] | length > 0 and\n    item.1.systems is true\n\n- name: \"{{ my_name }} - build internal directory layout (top level paths) (links)\"\n  ansible.builtin.file:\n    src: \"../../{{ item.1.generic }}/{{ item.0.src }}\"\n    dest: \"{{ retronas_path }}/{{ system_key }}/{{ item.1.name }}/{{ item.0.dest }}\"\n    owner: \"{{ retronas_user }}\"\n    group: \"{{ retronas_group }}\"\n    state: link\n  loop: \"{{ internal_symlinks|product(top_level_paths)|list }}\"\n  loop_control:\n    label: \"{{ item.0.src }}\"\n  when: internal_symlinks is defined and\n    top_level_paths is defined\n"
  },
  {
    "path": "ansible/roles/retronas.role.samba.system/tasks/main.yaml",
    "content": "---\n- name: \"{{ my_name }} - Include systems map\"\n  ansible.builtin.include_vars: \"retronas_systems.yml\"\n\n- name: \"{{ my_name }} - Load RetroNAS config\"\n  ansible.builtin.include_vars: retronas_vars.yml\n\n- name: \"{{ my_name }} - build top level\"\n  ansible.builtin.file:\n    path: \"{{ retronas_path }}/{{ system_key }}\"\n    owner: \"{{ retronas_user }}\"\n    group: \"{{ retronas_group }}\"\n    state: directory\n    mode: \"0775\"\n\n- name: \"{{ my_name }} - build top level share paths\"\n  ansible.builtin.file:\n    path: \"{{ retronas_path }}/{{ system_key }}/{{ item.name }}\"\n    owner: \"{{ retronas_user }}\"\n    group: \"{{ retronas_group }}\"\n    state: directory\n    mode: \"0775\"\n  loop: \"{{ top_level_paths }}\"\n  when: top_level_paths is defined and\n    item.enabled is true\n\n- name: \"{{ my_name }} - setup directories with symlinks\"\n  include_tasks: link.yml\n  when: nolink is not defined or nolink is false\n\n- name: \"{{ my_name }} - setup directories without symlinks\"\n  include_tasks: nolink.yml\n  when: nolink is defined and nolink is true\n\n- name: \"{{ my_name }} - configure includes file\"\n  ansible.builtin.ini_file:\n    path: /etc/samba/smb.conf\n    section: \"{{ system_key }}\"\n    option: \"include\"\n    value: \"/etc/samba/retronas_{{ system_key }}.conf\"\n\n- name: \"{{ my_name }} - configure retro shares\"\n  ansible.builtin.template:\n    src: \"retronas_system.conf.j2\"\n    dest: /etc/samba/retronas_{{ system_key }}.conf\n    owner: root\n    group: root\n    mode: \"0644\"\n\n- name: \"{{ my_name }} - build systems directory layout (systems)\"\n  ansible.builtin.shell:\n    cmd: \"{{ path_list_ll|join('') }}\"\n  when: path_list_ll is defined\n  changed_when: false\n\n- name: \"{{ my_name }} - build systems directory layout (systems) (top_level)\"\n  ansible.builtin.shell:\n    cmd: \"{{ path_list_tl|join('') }}\"\n  when: path_list_tl is defined\n  changed_when: false\n"
  },
  {
    "path": "ansible/roles/retronas.role.samba.system/tasks/nolink.yml",
    "content": "---\n- name: \"{{ my_name }} - build layout nolink list (set fact) (dir)\"\n  ansible.builtin.set_fact:\n    path_list_ll: \"{{ path_list_ll|default([]) + [ 'mkdir -p ' + retronas_path + '/' + system_key + '/' + item[system_key] + ';' ] }}\"\n  loop: \"{{ system_map }}\"\n  loop_control:\n    label: \"{{ item[system_key] }}\"\n  when: top_level_paths is not defined and\n    item[system_key] is defined and\n    item[system_key] | length > 0\n\n- name: \"{{ my_name }} - build layout nolink list (set fact) (top level paths) (dir)\"\n  ansible.builtin.set_fact:\n    path_list_tl: \"{{ path_list_tl|default([]) + [ 'mkdir -p ' + retronas_path + '/' + system_key + '/' + item.1.name + '/' + item.0[system_key] + ';' ] }}\"\n  loop: \"{{ system_map|product(top_level_paths)|list }}\"\n  loop_control:\n    label: \"{{ item.0[system_key] }}\"\n  when: top_level_paths is defined and\n    item.1.enabled is true and\n    item.0[system_key] is defined and\n    item.0[system_key] | length > 0 and\n    item.1.systems is true\n"
  },
  {
    "path": "ansible/roles/retronas.role.samba.system/templates/retronas_system.conf.j2",
    "content": "## RetroNAS config.\n## This is autogenerated. Changes will be lost.\ncomment = {{ system_key }}\npath = {{ retronas_path }}/{{ system_key }}\nguest ok = no\nbrowseable = yes\nwrite list = {{ retronas_user }}\nwriteable = {{ writable | default('yes') }}\nvalid users = {{ retronas_user }}\ncreate mask = 0775\ndirectory mask = 0775\nfollow symlinks = yes\nwide links = yes\n"
  },
  {
    "path": "ansible/roles/retronas.role.sslcert/tasks/main.yaml",
    "content": "- name: \"{{ my_name }} - Load RetroNAS config\"\n  ansible.builtin.include_vars: retronas_vars.yml\n\n- ansible.builtin.import_role:\n    name: retronas.role.package.latest\n\n- name: \"{{ my_name }} - check self-signed cert\"\n  ansible.builtin.stat:\n    path: \"{{ my_cert }}\"\n  register: self_signed_cert\n\n- name: \"{{ my_name }} - create self-signed cert\"\n  ansible.builtin.shell:\n    cmd: /usr/bin/openssl req -nodes -new -x509  -keyout {{ my_key }} -out {{ my_cert }} -subj \"/C=AU/ST=Sydney/L=retronas/O=retronas/OU=retronas/CN=retronas\" -days 3660\n  when: self_signed_cert is defined and\n        self_signed_cert.stat.exists is false\n"
  },
  {
    "path": "ansible/roles/retronas.role.sslcert/vars/main.yaml",
    "content": "my_name: \"sslcert\"\nmy_file: \"install_{{ my_name }}\"\nmy_cert: /etc/ssl/private/retronas.crt\nmy_key: /etc/ssl/private/retronas.key\n\npackages:\n  - \"openssl\"\n"
  },
  {
    "path": "ansible/roles/retronas.role.system-config/tasks/main.yaml",
    "content": "---\n- name: \"{{ my_name }} - Load RetroNAS config\"\n  ansible.builtin.include_vars: retronas_vars.yml\n\n- name: \"{{ my_name }} - Gather required facts\"\n  ansible.builtin.setup:\n    gather_subset:\n      - date_time\n      - architecture\n      - default_ipv4\n\n- name: \"{{ my_name }} - Create retronas user system dirs\"\n  ansible.builtin.file:\n    path: \"{{ retronas_path }}/{{ item }}\"\n    owner: \"{{ retronas_user }}\"\n    group: \"{{ retronas_group }}\"\n    state: directory\n    mode: \"0755\"\n  with_items: \"{{ system_user_dirs }}\"\n\n- name: \"{{ my_name }} - Log General Entry\"\n  ansible.builtin.ini_file:\n    path: \"{{ retronas_path }}/config/{{ ini_filename }}\"\n    section: \"general\"\n    option: \"{{ item.option }}\"\n    value: \"{{ item.value }}\"\n    state: present\n  with_items: \"{{ general_options }}\"\n  when: general_options is defined\n\n- name: \"{{ my_name }} - Log Platform info\"\n  ansible.builtin.ini_file:\n    path: \"{{ retronas_path }}/config/{{ ini_filename }}\"\n    section: \"platform\"\n    option: \"{{ item.option }}\"\n    value: \"{{ item.value }}\"\n    state: present\n  with_items: \"{{ platform_info }}\"\n  when: platform_info is defined\n\n- name: \"{{ my_name }} - RetroNAS runtime info - git branch\"\n  ansible.builtin.shell:\n    cmd: git rev-parse --abbrev-ref HEAD\n    chdir: \"{{ retronas_root }}\"\n  register: retronas_git_branch\n\n- name: \"{{ my_name }} - RetroNAS runtime info - git commit\"\n  ansible.builtin.shell:\n    cmd: git rev-parse HEAD\n    chdir: \"{{ retronas_root }}\"\n  register: retronas_git_commit\n\n- name: \"{{ my_name }} - Log RetroNAS git branch\"\n  ansible.builtin.ini_file:\n    path: \"{{ retronas_path }}/config/{{ ini_filename }}\"\n    section: \"retronas\"\n    option: \"branch\"\n    value: \"{{ retronas_git_branch.stdout }}\"\n    state: present\n  when: retronas_git_branch is defined\n\n- name: \"{{ my_name }} - Log RetroNAS git commit\"\n  ansible.builtin.ini_file:\n    path: \"{{ retronas_path }}/config/{{ ini_filename }}\"\n    section: \"retronas\"\n    option: \"commit\"\n    value: \"{{ retronas_git_commit.stdout }}\"\n    state: present\n  when: retronas_git_commit is defined\n\n- name: \"{{ my_name }} - Log Package Entry\"\n  ansible.builtin.ini_file:\n    path: \"{{ retronas_path }}/config/{{ ini_filename }}\"\n    section: \"package\"\n    option: \"{{ module_name }}\"\n    value: \"{{ module_state }}\"\n    state: \"{{ module_state }}\"\n  when: module_name is defined and\n        module_state is defined\n\n- name: \"{{ my_name }} - fix config perms\"\n  ansible.builtin.file:\n    path: \"{{ retronas_path }}/config/{{ ini_filename }}\"\n    owner: \"{{ retronas_user }}\"\n    group: \"{{ retronas_group }}\"\n    state: file\n    mode: \"0644\"\n"
  },
  {
    "path": "ansible/roles/retronas.role.system-config/vars/main.yaml",
    "content": "---\nmy_name: \"retronas user config setup\"\nmodule_state: \"present\"\n\nsystem_user_dirs:\n  - config\n  - config/modules\n\ngeneral_options:\n  - { option: \"title\", value: \"RetroNAS Packages\"}\n  - { option: \"author\", value: \"Generated by RetroNAS\"}\n  - { option: \"last_updated\", value: \"{{ ansible_date_time.date }}\" }\n\nplatform_info:\n  - { option: \"architecture\", value: \"{{ ansible_architecture }}\" }\n  - { option: \"ipv4addr\", value: \"{{ ansible_default_ipv4.address }}\" }\n  - { option: \"ipv4iface\", value: \"{{ ansible_default_ipv4.interface }}\" }\n\nini_filename: \"retronas_packages.ini\"\n"
  },
  {
    "path": "ansible/roles/retronas.role.templates/tasks/main.yaml",
    "content": "---\n- name: \"{{ my_name }} - templates\"\n  ansible.builtin.template:\n    src: \"templates/{{ my_file }}/{{ tmpl.sub + '/' if tmpl.sub is defined else '' }}{{ tmpl.name }}.j2\"\n    dest: \"{{ tmpl.dest }}/{{ tmpl.name }}\"\n    owner: \"{{ tmpl.owner|default('root') }}\"\n    group: \"{{ tmpl.group|default('root') }}\"\n    mode: \"{{ tmpl.mode|default('0644') }}\"\n    force: \"{{ tmpl.force|default('yes') }}\"\n  loop: \"{{ templates }}\"\n  loop_control:\n    loop_var: \"tmpl\"\n"
  },
  {
    "path": "ansible/roles/retronas.role.update-user/tasks/main.yaml",
    "content": "- name: \"Load RetroNAS config\"\n  ansible.builtin.include_vars: retronas_vars.yml\n\n- name: \"update user\"\n  ansible.builtin.user:\n    user: \"{{ retronas_user }}\"\n    groups: \"{{ append_user_group }}\"\n    append: true\n  when: append_user_group is defined"
  },
  {
    "path": "ansible/roles/retronas.role.x11vnc/tasks/main.yaml",
    "content": "- ansible.builtin.import_role:\n    name: retronas.role.package.latest\n\n- ansible.builtin.import_role:\n    name: retronas.role.templates\n"
  },
  {
    "path": "ansible/roles/retronas.role.x11vnc/templates/install_x11vnc/x11vnc_wrapper.sh.j2",
    "content": "#!/bin/bash\n\nXDISPLAY=$1\nXAUTH=$2\nPORT=$3\n\nRFBAUTH=/etc/vncpasswd_retronas\n\n# use a local auth file if it exists\nif [ -f /home/$USER/vncpasswd_retronas ]\nthen\n    RFBAUTH=/home/$USER/vncpasswd_retronas\nfi\n\nx11vnc -quiet -display $XDISPLAY -auth $XAUTH -listen 0.0.0.0 -rfbport $PORT -rfbauth $RFBAUTH -forever\n"
  },
  {
    "path": "ansible/roles/retronas.role.x11vnc/vars/main.yaml",
    "content": "my_name: \"x11vnc\"\nmy_file: \"install_{{ my_name }}\"\n\npackages:\n  - xvfb\n  - x11vnc\n\ntemplates:\n  - { name: \"x11vnc_wrapper.sh\", dest: \"/usr/local/bin\", mode: \"0755\"}\n"
  },
  {
    "path": "ansible/templates/install_3ds_qr_codes/3ds_qr.sh.j2",
    "content": "#!/bin/bash\n\n## RetroNAS autogenerated\n\nFLUSH=\"\"\nOPTSTRING=\"f\"\n\nwhile getopts $OPTSTRING ARG\ndo\n  case $ARG in\n    f)\n      FLUSH=1\n      ;;\n  esac\ndone\n\nMYIP=$(ip -4 -br a show dev {{ retronas_net_3dsqr_interface }} | awk '!/127/{sub(/\\/[0-9].+$/, \"\"); print $3}' | head -n1)\nif [ -z \"${MYIP}\" ]\nthen \n  echo \"Failed to find ip address from interface, fix interface in retronas config\"\n  echo \"Press any key to continue\"\n  read -s\n  exit 1\nfi\n\nMYURL=\"http://${MYIP}/3ds\"\numask 002\n\ngenerate_qr () {\n  # Build path\n  mkdir -p \"${QRDIR}\" 2>/dev/null\n  # Convert URLs to HTML safe encoding:\n  CIAURL=$( echo \"${MYURL}/cia/$RELDIR/${BASENAME}\" | sed 's/\\ /%20/g' )\n  QRURL=$( echo \"${MYURL}/qr/$RELDIR/${BARENAME}.png\" | sed 's/\\ /%20/g' )\n  # Generate QR code\n  echo \"Generating QR for ${BARENAME} ...\"\n  qrencode -o \"${QRDIR}/${BARENAME}.png\" -s 10 -8 \"${CIAURL}\"\n  # Generate HTML file\n  echo \"\\\n  <html>\\\n  <center>\\\n  <br><br>\\\n  \"${CIAURL}\"\\\n  <br><br>\\\n  <img src=\"${BARENAME}.png\" border=\"0\">\\\n  <br><br>\" > \"${QRDIR}/${BARENAME}\".html\n}\n\nflush() {\n  echo \"Flushing QR codes\"\n  rm -f {{ retronas_path }}/3ds/qr/*\n}\n\nclean() {\n  ## Clean QR codes\n  echo \"Cleaning out QR codes...\"\n  find \"{{ retronas_path }}/3ds/qr/\" -type f -iname '*.png' | while read QRFILE\n  do\n    BASENAME=$( basename \"${QRFILE}\" )\n    BARENAME=\"${BASENAME%.*}\"\n    BASEDIR=$( dirname \"${QRFILE}\" )\n    RELDIR=$( echo \"${BASEDIR}\" | sed 's#{{ retronas_path }}/3ds/qr##g' )\n    CIADIR=\"{{ retronas_path }}/3ds/cia/${RELDIR}\"\n    test -f \"${CIADIR}/${BARENAME}\".cia || test -f \"${CIADIR}/${BARENAME}\".CIA || rm -vf \"${BASEDIR}/${BARENAME}.png\" \"${BASEDIR}/${BARENAME}.html\"\n  done\n}\n\n[ ! -z $FLUSH ] && flush\nclean\n## Find CIA files to scan\necho \"Scanning for new CIA files, generating QR codes...\"\nfind \"{{ retronas_path }}/3ds/cia/\" -type f -iname '*.cia' | while read CIAFILE\ndo\n  BASENAME=$( basename \"${CIAFILE}\" )\n  BARENAME=\"${BASENAME%.*}\"\n  BASEDIR=$( dirname \"${CIAFILE}\" )\n  RELDIR=$( echo \"${BASEDIR}\" | sed 's#{{ retronas_path }}/3ds/cia##g' )\n  QRDIR=\"{{ retronas_path }}/3ds/qr/${RELDIR}\"\n  test -f \"${QRDIR}/${BARENAME}\".png || generate_qr\ndone\n\necho \"All done\"\nread -s\n"
  },
  {
    "path": "ansible/templates/install_3ds_qr_codes/retronas_3ds_qr.cron.j2",
    "content": "# RetroNAS automatically generated\n# Every 15 minutes check for new 3DS titles and generate a QR code to match\n*/15 * * * *   {{ retronas_user }}  {{ retronas_root }}/scripts/3ds_qr.sh >/dev/null 2>&1\n"
  },
  {
    "path": "ansible/templates/install_adtpro/ADTPro.properties.j2",
    "content": "#ADTPro.properties\nAudioHardwareIndex=0\nAudioPortIndex=0\nClient01xCompatibleProtocol=false\nCommPort=/dev/ttyUSB0\nCommPortBootstrapPacing=250\nCommPortBootstrapSpeed=9600\nCommPortSpeed=9600\nHardwareHandshaking=false\nSerialIPHost=0.0.0.0\nSerialIPPort=1977\nTraceEnabled=false\nUDPServerPort=6502\nWorkingDirectory={{ retronas_path }}/adtpro"
  },
  {
    "path": "ansible/templates/install_adtpro/adtpro.service.j2",
    "content": "[Unit]\nDescription=ADTPro %i\nStartLimitIntervalSec=60\nStartLimitBurst=4\n\n[Service]\nWorkingDirectory={{ my_dir }}\nExecStart={{ my_dir }}/adtpro_retronas.sh %i\nRestart=on-failure\nRestartSec=2\nSuccessExitStatus=3 4\nRestartForceExitStatus=3 4\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "ansible/templates/install_adtpro/adtpro_retronas.sh.j2",
    "content": "#!/bin/bash\n\nMODE=${1:-localhost}\nPORT=${2:-60000}\n\nPARENTPID=$(ps -ef | grep -E \"xvfb-run.*adtpro.*$MODE\" | grep -v grep | head -n1 | awk '{print $2}')\n\ncd {{ my_dir }}\n./adtpro.sh headless $MODE &> {{ retronas_root }}/log/adtpro_$MODE.log &\n\n\n# get atpro runtime details\nsleep 3\nSESSION=$(ps -ef | grep -E \"$PARENTPID.*Xvfb\" | head -n1 | awk '{print $9,$16}')\nXDISPLAY=$(echo $SESSION | cut -d' ' -f1 )\nXAUTH=$(echo $SESSION | cut -d' ' -f2 )\n\n/usr/local/bin/x11vnc_wrapper.sh $XDISPLAY $XAUTH $PORT\n"
  },
  {
    "path": "ansible/templates/install_adtpro/install_adtpro.sh.j2",
    "content": "#!/bin/bash\n\nset -u\n\nBINDIR=\"/opt/adtpro\"\nDLDIR=$(mktemp -d)\n\n\necho \"Downloading adtpro release...\"\nRELEASE=$( curl -kLs https://api.github.com/repos/ADTPro/adtpro/releases | jq -r \".[0].assets | map(select(.name | match (\\\".tar.gz\\\")))[-1] | .browser_download_url\" )\n[ -z \"$RELEASE\" ] && echo \"Couldn't get release file\" && exit 1\n\ncd $DLDIR\ncurl -OJL \"${RELEASE}\"\n\nRELFILE=$(basename $RELEASE)\n\nif [ -f $RELFILE ]\nthen\n    echo \"install dir\"\n    [ -d $BINDIR ] && mkdir -p $BINDIR\n\n    echo \"unpacking\"\n    tar xvf $DLDIR/$RELFILE \n    \n    echo \"moving\" \n    mv ADTPro-*/* $BINDIR/\n\n    echo \"cleaning up\"\n    rm -rf \"${DLDIR}\"\n\n\n    if [ -d $BINDIR/disks ]\n    then\n        echo \"copying disks to storage\"\n        chown -R {{ retronas_user }}: \"${BINDIR}/disks/\"\n        cp --no-clobber -p \"${BINDIR}/disks/\"* \"{{ retronas_path }}/roms/apple/appleii/\"\n    fi\nelse\n    echo \"file is not present\"\n    exit 1\nfi\n\necho \"All done!\"\n"
  },
  {
    "path": "ansible/templates/install_affstools/install_affstools.sh.j2",
    "content": "#!/bin/bash\n\nAPP=affstools\nSRCDIR=\"{{ retronas_root }}/src\"\nBINDIR=\"/usr/local/sbin\"\n\nBINS=(\n    affsck\n    mkaffs\n)\n\necho \"Configuring build directories...\"\nrm -rf \"${SRCDIR}\"\nmkdir -p \"${SRCDIR}\"\ncd \"${SRCDIR}\"\n\necho \"Downloading source code...\"\ngit clone https://github.com/kdave/${APP}\ncd ${APP}\n\n./configure\nmake\n\necho \"Moving binary to ${BINDIR}...\"\nfor BIN in ${BINS[@]}\ndo\n    chmod +x ${BIN}\n    mv -vf ${BIN} \"${BINDIR}\"/\ndone\n\necho \"Cleaning up...\"\nrm -rf \"${SRCDIR}\"\n\necho \"All done!\"\n"
  },
  {
    "path": "ansible/templates/install_amitools/install_amitools.sh.j2",
    "content": "#!/bin/bash\n\nAPP=amitools\nmv /usr/lib/python3*/EXTERNALLY-MANAGED /usr/lib/python3/EXTERNALLY-MANAGED.old 2> /dev/null\npip3 install cython\npip3 install -U git+https://github.com/cnvogelg/${APP}.git\nmv /usr/lib/python3*/EXTERNALLY-MANAGED.old /usr/lib/python3/EXTERNALLY-MANAGED 2> /dev/null\n\necho \"All done!\"\n"
  },
  {
    "path": "ansible/templates/install_apfs-fuse/install_apfs-fuse.sh.j2",
    "content": "#!/bin/bash\n\nset -u\n\nSRCDIR=\"{{ retronas_root }}/src\"\nBINDIR=\"/usr/local/bin\"\nREPO=https://github.com/sgan81/apfs-fuse.git\n\n\nif [ ! -x \"${BINDIR}/apfs-fuse\" ]\nthen\n    echo \"apfs-fuse not found, so we'll build it!\"\n\n    echo \"Configuring build directories...\"\n    [ -d ${SRCDIR} ] && rm -rf \"${SRCDIR}\"\n    mkdir -p \"${SRCDIR}\"\n    cd \"${SRCDIR}\"\n\n    echo \"Downloading git source ...\"\n    git clone $REPO\n    cd \"${SRCDIR}/$(basename $REPO .git)\"\n\n    echo \"Processing submodules\"\n    git submodule init\n    git submodule update\n\n    echo \"Building\"\n    mkdir build\n    cd build\n    cmake ..\n    ccmake . # Only if you want to change build options\n    make\n\n    echo \"Moving binary to RetroNAS bin dir...\"\n    mkdir -p \"${BINDIR}\" 2>/dev/null\n    mv -vf apfs-fuse \"${BINDIR}/\"\n\n    echo \"Cleaning up...\"\n    [ -d ${SRCDIR} ] && rm -rf \"${SRCDIR}\"\n\n    echo \"All done!\"\nfi\n\n"
  },
  {
    "path": "ansible/templates/install_assembly64/assembly64.service.j2",
    "content": "[Unit]\nDescription={{ my_name }}\nStartLimitIntervalSec=60\nStartLimitBurst=4\n\n[Service]\nUser={{ retronas_user }}\nWorkingDirectory={{ my_dir }}\nExecStart={{ my_dir }}/{{ my_name }}_retronas.sh start\nRestart=on-failure\nRestartSec=2\nSuccessExitStatus=3 4\nRestartForceExitStatus=3 4\n\n[Install]\nWantedBy=multi-user.target"
  },
  {
    "path": "ansible/templates/install_assembly64/assembly64_retronas.sh.j2",
    "content": "#!/bin/bash\n\nMODE=${1:-localhost}\nPORT=${2:-60001}\nexport DISPLAY=:100\nexport JAVA_HOME=/opt/java/jdk-22.0.2\nexport JFX_HOME=/opt/java/javafx-sdk-22.0.2\n\nPARENTPID=$(ps -ef | grep -E \"xvfb-run.*assembly64.*\" | grep -v grep | head -n1 | awk '{print $2}')\n\ncd {{ my_dir }}\nXvfb $DISPLAY -screen 0 1024x768x16 &\n./assembly64.sh $1 &\n\n# get assembly64 runtime details\nsleep 3\nSESSION=$(ps -ef | grep -E \"$PARENTPID.*Xvfb\" | head -n1 | awk '{print $9,$16}')\nXDISPLAY=$(echo $SESSION | cut -d' ' -f1 )\nXAUTH=$(echo $SESSION | cut -d' ' -f2 )\n\n/usr/local/bin/x11vnc_wrapper.sh $XDISPLAY $XAUTH $PORT"
  },
  {
    "path": "ansible/templates/install_assembly64/install_assembly64.sh.j2",
    "content": "#!/bin/bash\n\nARCH=$(uname -m)\nDLARCH=aarch64\nJAVA_VERS=22.0.2\n\ncase $ARCH in\n    x86_64)\n        DLARCH=x64\n        ;;\n    aarch64)\n        DLARCH=aarch64\n        ;;\n    *)\n        DLARCH=x86_64\nesac\n\n[ ! -d /opt/java ] && mkdir -p /opt/java\ncd /tmp\n\n### DEPS\n\n## OPENJDK\nif [ ! -d /opt/java/jdk-${JAVA_VERS} ]\nthen\n    OPENJDK=\"https://download.java.net/java/GA/jdk${JAVA_VERS}/c9ecb94cd31b495da20a27d4581645e8/9/GPL/openjdk-${JAVA_VERS}_linux-${DLARCH}_bin.tar.gz\"\n    curl -sLk -oopenjdk.tar.gz $OPENJDK\n    tar -xv -f openjdk.tar.gz -C /opt/java\n    rm -f openjdk.tar.gz\nelse\n    echo \"jdk ${JAVA_VERS} already installed\"\nfi\n\n## OPENJFX\nif [ ! -d /opt/java/javafx-sdk-${JAVA_VERS} ]\nthen\n    OPENJFX=\"https://download2.gluonhq.com/openjfx/${JAVA_VERS}/openjfx-${JAVA_VERS}_linux-${DLARCH}_bin-sdk.zip\"\n    curl -sLk -oopenjfx.zip $OPENJFX\n    unzip -qq -d /opt/java openjfx.zip\n    rm -f openjfx.zip\nelse\n    echo \"jfx ${JAVA_VERS} already installed\"\nfi\n\n## ASSEMBLY64\nif [ ! -f /opt/assembly64/assembly64.sh ]\nthen\n    ASSEMBLY=\"https://assembly64.hackerswithstyle.se/assembly/assembly64-shell.zip\"\n    curl -sLk -oassembly64.zip $ASSEMBLY\n    unzip -qq -d /opt assembly64.zip\n    rm -f assembly64.zip\nelse\n    echo \"assembly64 already installed\"\nfi"
  },
  {
    "path": "ansible/templates/install_atarist-sidecart/99-retronas-sidecart.conf.j2",
    "content": ""
  },
  {
    "path": "ansible/templates/install_atarist-sidecart/atarist-sidecart-generate-roms.sh.j2",
    "content": "#!/bin/bash\n\nMYURL=\"http://$(ip -4 -br a | awk '!/127/{sub(/\\/[0-9].+$/, \"\"); print $3}')/files/atarist/roms\"\numask 002\n\ncd {{ retronas_path }}/atarist/sidecart\npython {{ script_dest }} --path {{ retronas_path }}/atarist/roms --url ${MYURL}\n\nmv {{ retronas_path }}/atarist/roms/roms.json {{ retronas_path }}/atarist/sidecart/\nchown {{ retronas_user }}:{{ retronas_group }} {{ retronas_path }}/atarist/sidecart/*"
  },
  {
    "path": "ansible/templates/install_atarist-sidecart/atarist-sidecart-mirrordb.sh.j2",
    "content": "#!/bin/bash\n# grab a local copy of the the sidecart db\n\nBASE_URL=http://ataristdb.sidecart.xyz\nDBDIR={{ base_path }}/sidecart/db\nWAIT=1\n\n\n_dlfile() {\n    [ -f ${1}.csv ] && rm ${1}.csv\n    wget -c -nc -nH -x \"${BASE_URL}/${1}\"\n}\n\n# Update db first\n{{ retronas_root }}/scripts/atarist-sidecart-updatedb.sh\n\n# Process Floppies DB\ncd {{ base_path }}/floppies\nfor ENTRY in $(ls ${DBDIR})\ndo\n    for ITEM in $(cat \"${DBDIR}/${ENTRY}\" | awk -F';' '{print $6}' | sed 's/\"//g' | sed  's/\\r$//' )\n    do\n        _dlfile $ITEM\n        sleep ${WAIT}\n    done\ndone\n\n# Process ROMs json data\ncd /tmp\n_dlfile https://raw.githubusercontent.com/diegoparrilla/atarist-sidecart-raspberry-pico/main/roms/roms.json\n\n# dl ROMs\ncd {{ base_path }}/roms\nfor URL in $(cat /tmp/roms.json | jq -r '.[].url')\ndo\n    _dlfile $ITEM\n    sleep ${WAIT}\ndone\n\n[ -f /tmp/roms.json ] && rm -f /tmp/roms.json\nchown -R {{ retronas_user }}:{{ retronas_group }} {{ base_path }}\n"
  },
  {
    "path": "ansible/templates/install_atarist-sidecart/atarist-sidecart-updatedb.sh.j2",
    "content": "#!/bin/bash\n# grab a local copy of the the sidecart db\n\nBASE_URL=http://ataristdb.sidecart.xyz\nOUTPUTDIR={{ base_path }}/sidecart/db\n\ncd $OUTPUTDIR\n\n_dlfile() {\n    [ -f ${1}.csv ] && rm ${1}.csv\n    wget -c -w1 \"${BASE_URL}/db/${1}\"\n}\n\nfor LETTER in {a..z}\ndo\n  _dlfile ${LETTER}.csv\ndone\n\nfor NUMBER in {0..9}\ndo\n    _dlfile ${NUMBER}.csv\ndone\n\nchown -R {{ retronas_user }}:{{ retronas_group }} {{ retronas_path }}/atarist/db"
  },
  {
    "path": "ansible/templates/install_atarist-sidecart/index.html.j2",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n    <head>\n        <title>sidecart</title>\n    </head>\n    <style>\n    body {\n        font-family: fixed;\n    }\n    </style>\n    <body>\n        <h1>Sidecart - Atari ST</h1>\n        <div>\n            <h2>Configurator Settings</h2>\n            <table border=\"1\">\n                <thead>\n                    <th>Option</th>\n                    <th>Setting</th>\n                </thead>\n                <tbody>\n                    <tr><td>FLOPPY_DB_URL</td><td>http://retronas/files/atarist/sidecart</td></tr>\n                    <tr><td>ROMS_YAML_URL</td><td>http://retronas/files/atarist/sidecart/roms.json</td></tr>\n                </tbody>\n            </table>\n        </div>\n        <p></p>\n        <div>\n            <h2>Paths</h2>\n            <ul>\n                <li><a href=\"floppies\" title=\"\">floppies</a></li>\n                <li><a href=\"roms\" title=\"\">roms</a></li>\n            </ul>\n        </div>\n    </body>\n</html>"
  },
  {
    "path": "ansible/templates/install_atarist-sidecart/retronas_atarist.conf.j2",
    "content": "## RetroNAS config.\n## This is autogenerated. Changes will be lost.\ncomment = atarist\npath = {{ retronas_path }}/atarist\nguest ok = no\nbrowseable = yes\nwrite list = {{ retronas_user }}\nwriteable = yes\nvalid users = {{ retronas_user }}\ncreate mask = 0775\ndirectory mask = 0775\nfollow symlinks = yes\nwide links = yes"
  },
  {
    "path": "ansible/templates/install_cockpit-retronas/install_cockpit-retronas.sh.j2",
    "content": "#!/bin/bash\n#\n# Clone down additional Cockpit Packages\n#\n# eventually combine with cockpit-packages\n\nRNTMP=/tmp/rn_cpack\nCOCKPIT=/usr/share/cockpit\n\n# make out temp dir\n[ ! -d ${RNTMP} ] && mkdir ${RNTMP}\n\nfunction get_github {\n\n    local ACCOUNT=$1\n    local REPO=$2\n    local FOLDER=$3\n\n    cd $RNTMP\n    git clone https://github.com/${ACCOUNT}/${REPO}.git $FOLDER\n    cd $FOLDER\n    git checkout menu-changes\n\n}\n\n### there'll be a better way to approach this\nfunction get_retronas {\n\n    PACKAGES=(\n        cockpit-retronas\n    )\n\n    for PACKAGE in ${PACKAGES}\n    do\n        FOLDER=$(echo ${PACKAGE} | sed 's/^cockpit-//')\n        get_github retronas $PACKAGE $FOLDER\n        [ ! -d \"${RNTMP}/${FOLDER}\" ] && echo \"Can't find my temp dir man, boooo!\" && exit 1\n        cd \"${RNTMP}/${FOLDER}\"\n\n        RELEASE=$(git tag | tail -n1)\n        git checkout $RELEASE\n\n        cd ..\n  \n        cp -R \"${FOLDER}\" \"${COCKPIT}/\"\n        rm -Rf \"${RNTMP}/${FOLDER}/\"\n    done\n\n}\n\nget_retronas\n"
  },
  {
    "path": "ansible/templates/install_cue2pops/install_cue2pops.sh.j2",
    "content": "#!/bin/bash\n\n### SETUP\nAPP=cue2pops\nAPPREPO=makefu\n\nLOCALDIR=/opt/${APP}\nLOCALSCRIPT=/usr/local/bin/${APP}\n\nif [ -f ${LOCALDIR}/${APP} ]\nthen\n    echo \"Already compiled, nothing to do\"\n    exit\nfi\n\ncd /opt\ngit clone https://github.com/${APPREPO}/${APP}-linux.git $APP\ncd ${APP}\nmake\n\nif [ $? -eq 0 ]\nthen\n    ln -sf ${LOCALDIR}/${APP} $LOCALSCRIPT\nelse\n    echo \"Compilation failed\"\nfi\n\n"
  },
  {
    "path": "ansible/templates/install_deluge/auth.j2",
    "content": "deluge:deluge:10\n"
  },
  {
    "path": "ansible/templates/install_deluge/autoadd.conf.j2",
    "content": "{\n    \"file\": 2,\n    \"format\": 1\n}{\n    \"next_id\": 2,\n    \"watchdirs\": {\n        \"1\": {\n            \"abspath\": \"{{ retronas_path }}/bittorrent/auto-add\",\n            \"add_paused\": false,\n            \"add_paused_toggle\": false,\n            \"append_extension\": \".added\",\n            \"append_extension_toggle\": false,\n            \"auto_managed\": true,\n            \"auto_managed_toggle\": false,\n            \"copy_torrent\": \"{{ retronas_path }}/bittorrent/torrents\",\n            \"copy_torrent_toggle\": false,\n            \"delete_copy_torrent_toggle\": false,\n            \"download_location\": \"{{ retronas_path }}/bittorrent/downloading\",\n            \"download_location_toggle\": false,\n            \"enabled\": true,\n            \"label\": \"\",\n            \"label_toggle\": false,\n            \"max_connections\": 0,\n            \"max_connections_toggle\": false,\n            \"max_download_speed\": 0,\n            \"max_download_speed_toggle\": false,\n            \"max_upload_slots\": 0,\n            \"max_upload_slots_toggle\": false,\n            \"max_upload_speed\": 0,\n            \"max_upload_speed_toggle\": false,\n            \"move_completed\": true,\n            \"move_completed_path\": \"{{ retronas_path }}/bittorrent/complete\",\n            \"move_completed_toggle\": false,\n            \"owner\": \"{{ retronas_user }}\",\n            \"path\": \"{{ retronas_path }}/bittorrent/auto-add\",\n            \"queue_to_top\": true,\n            \"queue_to_top_toggle\": false,\n            \"remove_at_ratio\": false,\n            \"remove_at_ratio_toggle\": false,\n            \"seed_mode\": false,\n            \"stop_at_ratio\": false,\n            \"stop_at_ratio_toggle\": false,\n            \"stop_ratio\": 0,\n            \"stop_ratio_toggle\": false\n        }\n    }\n}"
  },
  {
    "path": "ansible/templates/install_deluge/core.conf.j2",
    "content": "{\n    \"file\": 1,\n    \"format\": 1\n}{\n    \"add_paused\": false,\n    \"allow_remote\": true,\n    \"auto_manage_prefer_seeds\": false,\n    \"auto_managed\": true,\n    \"autoadd_enable\": true,\n    \"autoadd_location\": \"{{ retronas_path }}/bittorrent/auto-add\",\n    \"cache_expiry\": 60,\n    \"cache_size\": 512,\n    \"compact_allocation\": false,\n    \"copy_torrent_file\": true,\n    \"daemon_port\": 58846,\n    \"del_copy_torrent_file\": false,\n    \"dht\": true,\n    \"dont_count_slow_torrents\": false,\n    \"download_location\": \"{{ retronas_path }}/bittorrent/downloading\",\n    \"download_location_paths_list\": [],\n    \"enabled_plugins\": [\n        \"AutoAdd\"\n    ],\n    \"enc_in_policy\": 1,\n    \"enc_level\": 2,\n    \"enc_out_policy\": 1,\n    \"enc_prefer_rc4\": true,\n    \"geoip_db_location\": \"/usr/share/GeoIP/GeoIP.dat\",\n    \"ignore_limits_on_local_network\": true,\n    \"info_sent\": 0.0,\n    \"listen_interface\": \"\",\n    \"listen_ports\": [\n        46000,\n        46000\n    ],\n    \"listen_random_port\": null,\n    \"listen_reuse_port\": true,\n    \"listen_use_sys_port\": false,\n    \"lsd\": true,\n    \"max_active_downloading\": -1,\n    \"max_active_limit\": -1,\n    \"max_active_seeding\": -1,\n    \"max_connections_global\": -1,\n    \"max_connections_per_second\": 50,\n    \"max_connections_per_torrent\": -1,\n    \"max_download_speed\": -1.0,\n    \"max_download_speed_per_torrent\": -1,\n    \"max_half_open_connections\": 50,\n    \"max_upload_slots_global\": -1,\n    \"max_upload_slots_per_torrent\": -1,\n    \"max_upload_speed\": -1.0,\n    \"max_upload_speed_per_torrent\": -1,\n    \"move_completed\": true,\n    \"move_completed_path\": \"{{ retronas_path }}/bittorrent/complete\",\n    \"move_completed_paths_list\": [],\n    \"natpmp\": true,\n    \"new_release_check\": false,\n    \"outgoing_interface\": \"\",\n    \"outgoing_ports\": [\n        44000,\n        45000\n    ],\n    \"path_chooser_accelerator_string\": \"Tab\",\n    \"path_chooser_auto_complete_enabled\": true,\n    \"path_chooser_max_popup_rows\": 20,\n    \"path_chooser_show_chooser_button_on_localhost\": true,\n    \"path_chooser_show_hidden_files\": false,\n    \"peer_tos\": \"0x00\",\n    \"plugins_location\": \"/usr/lib/python3/dist-packages/deluge/plugins\",\n    \"pre_allocate_storage\": true,\n    \"prioritize_first_last_pieces\": false,\n    \"proxy\": {\n        \"anonymous_mode\": false,\n        \"force_proxy\": false,\n        \"hostname\": \"\",\n        \"password\": \"\",\n        \"port\": 2525,\n        \"proxy_hostnames\": true,\n        \"proxy_peer_connections\": true,\n        \"proxy_tracker_connections\": true,\n        \"type\": 0,\n        \"username\": \"\"\n    },\n    \"queue_new_to_top\": false,\n    \"random_outgoing_ports\": true,\n    \"random_port\": false,\n    \"rate_limit_ip_overhead\": true,\n    \"remove_seed_at_ratio\": false,\n    \"seed_time_limit\": -1,\n    \"seed_time_ratio_limit\": -1.0,\n    \"send_info\": false,\n    \"sequential_download\": false,\n    \"share_ratio_limit\": -1.0,\n    \"shared\": false,\n    \"stop_seed_at_ratio\": true,\n    \"stop_seed_ratio\": -1.0,\n    \"super_seeding\": false,\n    \"torrentfiles_location\": \"{{ retronas_path }}/bittorrent/torrents\",\n    \"upnp\": true,\n    \"utpex\": true\n}"
  },
  {
    "path": "ansible/templates/install_deluge/deluge-web.service.j2",
    "content": "[Unit]\nDescription=Deluge Bittorrent Client Web Interface\nAfter=network-online.target\n\n[Service]\nType=simple\n\nUser={{ retronas_user }}\nGroup={{ retronas_user }}\nUMask=007\n\nExecStart=/usr/bin/deluge-web -d -c /var/lib/deluged/config\n\nRestart=on-failure\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "ansible/templates/install_deluge/deluged.j2",
    "content": "# Defaults for deluged initscript\n# sourced by /etc/init.d/deluged\nENABLE_DELUGED=1\n"
  },
  {
    "path": "ansible/templates/install_deluge/deluged.service.j2",
    "content": "[Unit]\nDescription=Deluge Daemon\nAfter=network-online.target\n\n[Service]\nType=simple\n\nUser={{ retronas_user }}\nGroup={{ retronas_user }}\nUMask=007\n\nExecStart=/usr/bin/deluged --do-not-daemonize --config /var/lib/deluged/config\n\nRestart=on-failure\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "ansible/templates/install_deluge/upgrade_deluge.sh.j2",
    "content": "#!/bin/bash\n\nENDPOINT=http://ftp.us.debian.org/debian/pool/main/d/deluge/\nVERSION=${1:-2.1.2~dev0+20240910-5_all.deb}\n\nPACKAGES=(\n  deluge-common\n  deluge-web\n  deluge-console\n  deluged\n)\n\nDTMPDIR=$(mktemp -d)\nDEBFILES=()\n\ncd $DTMPDIR\n\nfor PACKAGE in ${PACKAGES[@]}\ndo\n  PNAME=${PACKAGE}_${VERSION}\n  curl -s -O${PNAME} $ENDPOINT/${PNAME}\n  DEBFILES+=\" ./${PNAME}\"\ndone\n\nDEP=python3-legacy-cgi_2.6.2-1_all.deb\ncurl -sO http://ftp.us.debian.org/debian/pool/main/p/python-legacy-cgi/${DEP}\nDEBFILES+=\" ./${DEP}\"\n\napt update\napt install -y ${DEBFILES}\nrm -f ${DEBFILES}\n\nsed -i 's/debian-deluged/{{ retronas_user }}/' /usr/lib/systemd/system/deluged.service\nsed -i 's/debian-deluged/{{ retronas_user }}/' /usr/lib/systemd/system/deluge-web.service\n\n#usermod -a -G debian-deluged {{ retronas_user }}\nchown {{ retronas_user }}:{{ retronas_group }} -R /var/lib/deluged/\n\nsystemctl daemon-reload\nsleep 2\nsystemctl stop deluge*\nsystemctl restart deluge*"
  },
  {
    "path": "ansible/templates/install_disable-laptop-lid/retronas.conf.j2",
    "content": "[Login]\nHandleLidSwitch=ignore\nHandleLidSwitchExternalPower=ignore\nHandleLidSwitchDocked=ignore\nIdleAction=ignore"
  },
  {
    "path": "ansible/templates/install_disc-image-creator/install_disc-image-creator.sh.j2",
    "content": "#!/bin/bash\n\n### SETUP\nAPP=DiscImageCreator\nAPPREPO=saramibreak\nAPPLOW=$(echo $APP | tr [:upper:] [:lower:])\nAPPEXEC=${APP}\n\nECCEDC=/opt/eccedc/EccEdc_linux.out\nDVDAUTH=/opt/dvdauth/DVDAuth_linux.out\n\nLOCALTMP=$( mktemp -d /tmp/${APPLOW}-XXXX )\nLOCALFILE=/${LOCALTMP}/${APPLOW}.zip\nLOCALDIR=/opt/${APPLOW}\nLOCALSCRIPT=/usr/local/bin/${APPLOW}\nAPPFILES=${LOCALDIR}/*\n\nPATTERN=*archive.*zip\nGITHRELS=https://github.com/${APPREPO}/${APP}/tags\n\n### DISCOVERY\n#REDIRECT=$( curl -k -s ${GITHRELS} | awk -F '\"' '{print $2}' )\nRELEASE=$( curl -Lks \"${GITHRELS}\" | awk -F '\"' /href.*${PATTERN}/'{print $4}' | head -n1 )\n\n\n### DOWNLOAD\n# -- Download\n[ ! -f ${LOCALFILE} ] && curl -k -L -o ${LOCALFILE} \"https://github.com/${RELEASE}\"\n[ $? -ne 0 ] && echo \"Sadness getting ${APP}\" && exit 1\n\n\n### LOCAL\n[ ! -d ${LOCALDIR} ] && /bin/mkdir ${LOCALDIR} || echo \"Missing ${LOCALDIR}\"\n\n\n### UNPACK\n[ -f ${LOCALFILE} ] && /usr/bin/unzip -o ${LOCALFILE} -d ${LOCALTMP} || echo \"Missing: ${LOCALFILE}\"\n\n### BUILD\ncd ${LOCALTMP}/${APP}-*/${APP}\nmake\n\n### INSTALL\n[ -f ${LOCALTMP}/${APP}-*/${APP}/${APPEXEC} ] && mv ${LOCALTMP}/${APP}-*/${APP}/${APPEXEC} ${LOCALDIR}/\n\ncat << EOST > ${LOCALSCRIPT}\n#!/bin/bash\ncd ${LOCALDIR}\n${LOCALDIR}/${APPEXEC} $*\nEOST\n\n### PERMISSIONS\nchmod +x ${LOCALSCRIPT}\nchmod 755 ${LOCALSCRIPT}\n\n\n### TOOL DEPS (EccEdc, DVDAuth)\nln -sf ${ECCEDC} ${LOCALDIR}/\nln -sf ${DVDAUTH} ${LOCALDIR}/\n\n### ASSETS\nASSET_DIR=${LOCALTMP}/${APP}-*/Release_ANSI\ncp ${ASSET_DIR}/*.txt ${LOCALDIR}/\ncp -R ${ASSET_DIR}/Doc ${LOCALDIR}/\n\n\n### CLEANUP\nrm -rf ${LOCALTMP}"
  },
  {
    "path": "ansible/templates/install_dnsmasq-retro/dnsmasq-retro.service.j2",
    "content": "[Unit]\nDescription=DNS caching server retro. \nRequires=network.target\nWants=nss-lookup.target\nAfter=network.target\n\n[Service]\nExecStart=/usr/sbin/dnsmasq -k --conf-dir=/etc/dnsmasq.d/retro --pid-file=/var/run/dnsmasq-retro.pid\nRestartSec=10s\nTimeoutStartSec=8s\nRestart=on-failure\n\n[Install]\nWantedBy=multi-user.target"
  },
  {
    "path": "ansible/templates/install_dnsmasq-retro/retro/dhcp-retro-ethernet.conf.j2",
    "content": "interface={{ retronas_net_retro_interface }}\ndhcp-range={{ retronas_net_retro_interface }},{{ retronas_net_retro_dhcprange }}\ndhcp-option={{ retronas_net_retro_interface }},option:router,{{ retronas_net_retro_router }}\ndhcp-option={{ retronas_net_retro_interface }},option:ntp-server,{{ retronas_net_retro_ntp }}"
  },
  {
    "path": "ansible/templates/install_dnsmasq-retro/retro/dhcp-retro-wifi.conf.j2",
    "content": "interface={{ retronas_net_wifi_interface }}\ndhcp-range={{ retronas_net_wifi_interface }},{{ retronas_net_wifi_dhcprange }}\ndhcp-option={{ retronas_net_wifi_interface }},option:router,{{ retronas_net_wifi_router }}\ndhcp-option={{ retronas_net_wifi_interface }},option:ntp-server,{{ retronas_net_wifi_ntp }}"
  },
  {
    "path": "ansible/templates/install_dnsmasq-retro/retro/dhcp.conf.j2",
    "content": "dhcp-authoritative\ndhcp-leasefile=/tmp/dnsmasq-retro.leases\ndhcp-lease-max=50"
  },
  {
    "path": "ansible/templates/install_dnsmasq-retro/retro/dns.conf.j2",
    "content": "# google\nserver={{ retronas_net_upstream_dns1 }}\nserver={{ retronas_net_upstream_dns1 }}"
  },
  {
    "path": "ansible/templates/install_dnsmasq-retro/retro/dnsmasq.conf.j2",
    "content": "user=root\ngroup=root\nbogus-priv\ndomain-needed\nno-resolv\nall-servers\nexpand-hosts\ndomain=retro\nlocal=/retro/ \nlog-queries\nno-poll"
  },
  {
    "path": "ansible/templates/install_dnsmasq-retro/retro/interfaces.conf.j2",
    "content": "interface=lo\nbind-interfaces"
  },
  {
    "path": "ansible/templates/install_dnsmasq-retro/retro/ipv6.conf.j2",
    "content": "listen-address=::1"
  },
  {
    "path": "ansible/templates/install_dreampi/dreampi.conf.j2",
    "content": "domain-needed\nbogus-priv\nserver=46.101.91.123\nserver=209.50.50.129\nno-resolv\nno-hosts\ncache-size=500\nlog-queries\n"
  },
  {
    "path": "ansible/templates/install_dreampi/install_dreampi.sh.j2",
    "content": "#!/bin/bash\n\nset -e\n\n# based on https://github.com/Kazade/dreampi/issues/17\n\nDPITMP=$(mktemp -d)\n\n# clone the repo and switch to the python3 branch\ncd $DPITMP\ngit clone https://github.com/Kazade/dreampi.git\ncd dreampi\ngit checkout python3\n\n# add arch armhf\ndpkg --add-architecture armhf\napt-get update\n# workaround for if a previous install was broken due to missing arch\napt-get --fix-missing -y install\n\n# install dependancies\ndpkg -i arm/*.deb\n\n# pull python3 requirements\ncurl -O https://raw.githubusercontent.com/sairuk/dreampi/master/requirements.txt\nif [ ! -f /usr/lib/python3*/EXTERNALLY-MANAGED ]\nthen\n    python3 -m pip install -r requirements.txt\nelse\n    apt-get install -y python3-serial python3-sh python3-iptables python3-miniupnpc\n    # there are no system packages for this so ignore warning\n    python3 -m pip install http-server-base --break-system-packages\nfi\n\n# install dreampi\n[ ! -d /usr/local/share/dreampi ] && mkdir /usr/local/share/dreampi\ncp -f *.py dial-tone.wav /usr/local/share/dreampi\nln -sf /usr/local/share/dreampi/dreampi.py /usr/local/bin/dreampi\nchown -R root:root /usr/local/share/dreampi/\n\n# create startup scripts\ncp -f etc/init.d/dreampi /etc/init.d/\ncp -f etc/systemd/system/dreampi.service /etc/systemd/system/"
  },
  {
    "path": "ansible/templates/install_dvdauth/install_dvdauth.sh.j2",
    "content": "#!/bin/bash\n\n### SETUP\nAPP=DVDAuth\nAPPREPO=saramibreak\nAPPLOW=$(echo $APP | tr [:upper:] [:lower:])\nAPPEXEC=${APP}.out\n\nLOCALTMP=$( mktemp -d /tmp/${APPLOW}-XXXX )\nLOCALFILE=${LOCALTMP}/${APPLOW}.zip\nLOCALDIR=/opt/${APPLOW}\nLOCALSCRIPT=/usr/local/bin/${APPLOW}\nAPPFILES=${LOCALDIR}/*\n\n\n### DISCOVERY\ncd ${LOCALTMP}\ngit clone https://github.com/${APPREPO}/${APP}\ncd ${LOCALTMP}/${APP}\nRELEASE=$(git tag | tail -n1)\n\n\n### LOCAL\n[ ! -d ${LOCALDIR} ] && /bin/mkdir ${LOCALDIR} || echo \"Missing ${LOCALDIR}\"\n\n\n### BUILD\ncd ${LOCALTMP}/${APP}/${APP}\n#git checkout ${RELEASE}  ### can't do this yet, linux makefile isn't in a release\nmake\n\n### INSTALL\n[ -f ${LOCALTMP}/${APP}/${APP}/${APPEXEC} ] && mv ${LOCALTMP}/${APP}/${APP}/${APPEXEC} ${LOCALDIR}/\n\n\ncat << EOST > ${LOCALSCRIPT}\n#!/bin/bash\ncd ${LOCALDIR}\n${LOCALDIR}/${APPEXEC} \\$*\nEOST\n\n### PERMISSIONS\nchmod +x ${LOCALSCRIPT}\nchmod 755 ${LOCALSCRIPT}\n\n### CLEANUP\nrm -rf ${LOCALTMP}\n"
  },
  {
    "path": "ansible/templates/install_eccedc/install_eccedc.sh.j2",
    "content": "#!/bin/bash\n\n### SETUP\nAPP=EccEdc\nAPPREPO=saramibreak\nAPPLOW=$(echo $APP | tr [:upper:] [:lower:])\nAPPEXEC=${APP}.out\n\nLOCALTMP=$( mktemp -d /tmp/${APPLOW}-XXXX )\nLOCALFILE=${LOCALTMP}/${APPLOW}.zip\nLOCALDIR=/opt/${APPLOW}\nLOCALSCRIPT=/usr/local/bin/${APPLOW}\nAPPFILES=${LOCALDIR}/*\n\nPATTERN=*archive.*zip\nGITHRELS=https://github.com/${APPREPO}/${APP}/tags\n\n### DISCOVERY\n#REDIRECT=$( curl -k -s ${GITHRELS} | awk -F '\"' '{print $2}' )\nRELEASE=$( curl -Lks \"${GITHRELS}\" | awk -F '\"' /href.*${PATTERN}/'{print $4}' | head -n1 )\n\n\n### DOWNLOAD\n# -- Download\n[ ! -f ${LOCALFILE} ] && curl -k -L -o ${LOCALFILE} \"https://github.com/${RELEASE}\"\n[ $? -ne 0 ] && echo \"Sadness getting ${APP}\" && exit 1\n\n\n### LOCAL\n[ ! -d ${LOCALDIR} ] && /bin/mkdir ${LOCALDIR} || echo \"Missing ${LOCALDIR}\"\n\n\n### UNPACK\n[ -f ${LOCALFILE} ] && /usr/bin/unzip -o ${LOCALFILE} -d ${LOCALTMP} || echo \"Missing: ${LOCALFILE}\"\n\n### BUILD\ncd ${LOCALTMP}/${APP}-*/${APP}\nmake\n\n### INSTALL\n[ -f ${LOCALTMP}/${APP}-*/${APP}/${APPEXEC} ] && mv ${LOCALTMP}/${APP}-*/${APP}/${APPEXEC} ${LOCALDIR}/\n\n\ncat << EOST > ${LOCALSCRIPT}\n#!/bin/bash\ncd ${LOCALDIR}\n${LOCALDIR}/${APPEXEC} $*\nEOST\n\n### PERMISSIONS\nchmod +x ${LOCALSCRIPT}\nchmod 755 ${LOCALSCRIPT}\n\n### CLEANUP\nrm ${LOCALFILE}\nrm -rf ${LOCALTMP}\n"
  },
  {
    "path": "ansible/templates/install_etherdfs/etherdfs.service.j2",
    "content": "[Unit]\nDescription=EtherDFS lightweight file server for MS-DOS\nAfter=network.target\nStartLimitIntervalSec=60\nStartLimitBurst=4\n\n[Service]\nUmask=0000\nUser={{ retronas_user }}\nExecStart={{ retronas_root }}/bin/ethersrv-linux -f {{ retronas_etherdfs_interface }} {{ retronas_path}}/dos\nRestart=on-failure\nRestartSec=1\nSuccessExitStatus=3 4\nRestartForceExitStatus=3 4\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "ansible/templates/install_etherdfs/install_etherdfs.sh.j2",
    "content": "#!/bin/bash\n\nSRCDIR=\"{{ retronas_root }}/src\"\nBINDIR=\"{{ retronas_root }}/bin\"\n\necho \"Configuring build directories...\"\nrm -rf \"${SRCDIR}\"\nmkdir -p \"${SRCDIR}\"\ncd \"${SRCDIR}\"\n\necho \"Downloading EtherDFS source...\"\ngit clone 'https://github.com/BrianHoldsworth/etherdfs-server.git'\n\ncd etherdfs-server\nmake\n\necho \"Moving binary to bindir...\"\nmv -vf ethersrv-linux \"${BINDIR}\"/\n\necho \"Setting binary sticky bit...\"\nchmod -v u+s \"${BINDIR}\"/ethersrv-linux\n\necho \"Cleaning up...\"\n\nrm -rf \"${SRCDIR}\"\n\necho 'All done!'\n\n"
  },
  {
    "path": "ansible/templates/install_etherdfs/retronas_dos.conf.j2",
    "content": "## RetroNAS config.\n## This is autogenerated. Changes will be lost.\ncomment = dos\npath = {{ retronas_path }}/dos\nguest ok = no\nbrowseable = yes\nwrite list = {{ retronas_user }}\nwriteable = yes\nvalid users = {{ retronas_user }}\ncreate mask = 0775\ndirectory mask = 0775\nfollow symlinks = yes\nwide links = yes\nstrict sync = no\nsync always = yes\n"
  },
  {
    "path": "ansible/templates/install_ethflopd/ethflopd.service.j2",
    "content": "[Unit]\nDescription=ethflopd L2 floppy emulator server for MS-DOS\nAfter=network.target\nStartLimitIntervalSec=60\nStartLimitBurst=4\n\n[Service]\nUmask=0000\nUser={{ retronas_user }}\nExecStart={{ retronas_root }}/bin/ethflopd -f {{ retronas_etherdfs_interface }} {{ retronas_path}}/dos\nRestart=on-failure\nRestartSec=1\nSuccessExitStatus=3 4\nRestartForceExitStatus=3 4\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "ansible/templates/install_ethflopd/install_ethflopd.sh.j2",
    "content": "#!/bin/bash\n\nSRCDIR=\"{{ retronas_root }}/src\"\nBINDIR=\"{{ retronas_root }}/bin\"\nAPPNAME=ethflopd\nVERSION=20240916\n\necho \"Configuring build directories...\"\nrm -rf \"${SRCDIR}\"\nmkdir -p \"${SRCDIR}/${APPNAME}\"\ncd \"${SRCDIR}\"\n\necho \"Downloading ethflopd source...\"\ncurl -L -o ${APPNAME}.tar.gz https://master.dl.sourceforge.net/project/ethflop/ethflopd%20%28server%29/${VERSION}/ethflopd-${VERSION}-src.tar.gz\ntar -xvf ${APPNAME}.tar.gz -C ${APPNAME}\n\ncd ${APPNAME}\n\n[ ! -f Makefile.linux ] && echo \"Makefile not found, cannot build\" && exit 1\nmake -f Makefile.linux\n\necho \"Moving binary to bindir...\"\nmv -vf ${APPNAME} \"${BINDIR}\"/\n\necho \"Setting binary sticky bit...\"\nchmod -v u+s \"${BINDIR}\"/${APPNAME}\n\necho \"Cleaning up...\"\n\nrm -rf \"${SRCDIR}\"\n\necho 'All done!'\n\n"
  },
  {
    "path": "ansible/templates/install_extract-xiso/install_extract-xiso.sh.j2",
    "content": "#!/bin/bash\n\nset -u\n\nRN_BIN=/usr/local/bin/\n\nAPP=extract-xiso\nOUTPATH=/tmp/$APP\nREPO=https://github.com/XboxDev/extract-xiso.git\nXC=$APP\n\nfunction _log {\n\techo \"$1\"\n}\n\n### REQUIREMENTS\nREQFAIL=0\n[ $REQFAIL -ne 0 ] && _log \"Requirements failed, see previous errors\" && exit $REQFAIL\n\n### CLONE \nRESULT=\"FAILURE\"\n_log \"Attempting to clone ${REPO}\"\n[ ! -d ] && mkdir -p $OUTPATH\ncd $OUTPATH\ngit clone $REPO\n\n### COMPILE\ncd $APP\nmkdir build\ncd build\ncmake ..\n\n# MAKE\nmake\n\n[ -x ${XC} ] && mv ${XC} \"${RN_BIN}/\"\nchmod 755 ${RN_BIN}/${XC}\n\n# CLEAN UP\nrm -Rf ${OUTPATH}/"
  },
  {
    "path": "ansible/templates/install_far2l/install_far2l.sh.j2",
    "content": "#!/bin/bash\n\nset -u\n\nSRCDIR=\"{{ retronas_root }}/src\"\nBINDIR=\"/usr/local/bin\"\nREPO=https://github.com/elfmz/far2l\n\n\necho \"Configuring build directories...\"\n[ -d ${SRCDIR} ] && rm -rf \"${SRCDIR}\"\nmkdir -p \"${SRCDIR}\"\ncd \"${SRCDIR}\"\n\necho \"Downloading git source ...\"\ngit clone $REPO\ncd \"${SRCDIR}/$(basename $REPO .git)\"\ngit checkout $(git tag | tail -n1)\n\necho \"Building\"\nmkdir _build\ncd _build\ncmake -DUSEWX=no -DCMAKE_BUILD_TYPE=Release ..\ncmake --build . -j$(nproc --all)\nsudo cmake --install .\n\necho \"Cleaning up...\"\n[ -d ${SRCDIR} ] && rm -rf \"${SRCDIR}\"\n\necho \"All done!\""
  },
  {
    "path": "ansible/templates/install_fenrir-ode-webserver/fenrir-ode-webserver.service.j2",
    "content": "[Unit]\nDescription={{ my_name }}\nAfter=network.target\nStartLimitIntervalSec=60\nStartLimitBurst=4\n\n[Service]\nUser={{ retronas_user }}\nExecStart={{ fenrir_ode_bin }} --dir \"{{ fenrir_ode_path }}\" --port {{ fenrir_ode_port }}\nRestart=on-failure\nRestartSec=5\n\n[Install]\nWantedBy=multi-user.target"
  },
  {
    "path": "ansible/templates/install_fenrir-ode-webserver/install_fenrir-ode-webserver.sh.j2",
    "content": "#!/bin/bash\n\nset -u\n\nRN_BIN=/usr/local/bin/\n\nAPP=webserver\nREPO=https://github.com/Fenrir-ode/${APP}.git\nOUTPATH=/tmp\n\nfunction _log {\n\techo \"$1\"\n}\n\n### REQUIREMENTS\nREQFAIL=0\n#[ ! -x /usr/bin/gmake ] && _log \"GMAKE not found\" && REQFAIL=1\n[ $REQFAIL -ne 0 ] && _log \"Requirements failed, see previous errors\" && exit $REQFAIL\n\n\ncd $OUTPATH\nif [ ! -f ${OUTPATH}/${APP}/.git/config ] \nthen\n\tgit clone $REPO\nelse\n\tcd ${OUTPATH}/${APP}\n\tgit pull\nfi\n\ncd ${OUTPATH}/${APP}\ngit submodule init\ngit submodule update --recursive\n\n\nmkdir build\ncd build\ncmake ..\nmake\n\nmv ${OUTPATH}/${APP}/build/FenrirServer $RN_BIN/\n\n\n# CLEAN UP\n#rm -Rf ${OUTPATH}/${APP}\n"
  },
  {
    "path": "ansible/templates/install_firewalld/workarounds/clear-python-bytecode.j2",
    "content": "#!/bin/bash\n\nset -u\n\n#\n# firewalld will not start if corrupted bytecode exists for a dependent library\n\n# normally this is a none issue with standard python apps but for a firewall it is \n# a fairly bad scenario\n#\n# may be caused by a loss of power to a device\n#\n# clear out all bytecode from the python lib dir\n#\n\nPYTHON_LIBDIRS=(\n    /usr/lib/python*/\n    /usr/local/lib/python*/\n)\n\nfor PYTHON_LIBDIR in ${PYTHON_LIBDIRS[@]}\ndo\n    find $PYTHON_LIBDIR -type f -regex \".*\\.pyc$\" -exec rm \"{}\" \\;\ndone"
  },
  {
    "path": "ansible/templates/install_firewalld/workarounds/override.conf.j2",
    "content": "# /etc/systemd/system/firewalld.service.d/override.conf\n[Service]\nExecStartPre=-/usr/local/sbin/clear-python-bytecode\nExecStart=\nExecStart=/usr/sbin/firewalld --nofork --nopid"
  },
  {
    "path": "ansible/templates/install_firewalld-zones/policies/retro_to_modern.xml.j2",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<policy target=\"DROP\">\n  <ingress-zone name=\"retro\"/>\n  <egress-zone name=\"modern\"/>\n</policy>"
  },
  {
    "path": "ansible/templates/install_firewalld-zones/services/ps3netsrv.xml.j2",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<service>\n  <port port=\"38008\" protocol=\"tcp\"/>\n  <port port=\"38008\" protocol=\"udp\"/>\n</service>"
  },
  {
    "path": "ansible/templates/install_firewalld-zones/services/samba-modern.xml.j2",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<service>\n  <short>Samba Modern</short>\n  <description>Only allows 445/tcp</description>\n  <port protocol=\"tcp\" port=\"445\"/>\n</service>"
  },
  {
    "path": "ansible/templates/install_firewalld-zones/zones/modern.xml.j2",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<zone target=\"%%REJECT%%\">\n  <short>Modern</short>\n  <description>Connection out to your main Modern network</description>\n  <service name=\"cockpit\"/>\n  <service name=\"samba-modern\"/>\n  <service name=\"ssh\"/>\n  <masquerade/>\n  <interface name=\"{{ retronas_net_modern_interface}}\"/>\n</zone>"
  },
  {
    "path": "ansible/templates/install_firewalld-zones/zones/retro.xml.j2",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<zone target=\"%%REJECT%%\">\n  <short>retro</short>\n  <description>retro device zone</description>\n  <service name=\"dhcp\"/>\n  <service name=\"dns\"/>\n  <service name=\"ntp\"/>\n  <service name=\"mdns\"/>\n  <protocol value=\"icmp\"/>\n  <interface name=\"{{ retronas_net_retro_interface}}\"/>\n</zone>"
  },
  {
    "path": "ansible/templates/install_flippydrive/flippydrive.service.j2",
    "content": "[Unit]\nDescription=Flippydrive Network Service\nDocumentation=https://docs.flippydrive.com/network.html\nRequires=network-online.target\nAfter=network-online.target\n\n[Service]\nUser={{ retronas_user }}\nType=simple\nExecStart=/opt/flippydrive/flippydrive.sh\nTimeoutStopSec=10\nRestart=on-failure\n\n[Install]\nWantedBy=default.target\n"
  },
  {
    "path": "ansible/templates/install_flippydrive/flippydrive.sh.j2",
    "content": "#!/bin/bash\n\ncd /opt/flippydrive\n\nARCH=$(uname -m)\n\ncase $ARCH in\n  x86_64)\n    ARCH=amd64\n    ;;\n  *)\n    echo \"Unsupported architecture: $ARCH\"\n    exit 1\nesac\n\nLASTREL=$(ls -rt flippy_cli_*_linux_${ARCH} | tail -n1)\nif [ ! -z $LASTREL ]\nthen\n  [ ! -x $LASTREL ] && chmod +x $LASTREL\n  ./$LASTREL {{ retronas_path }}/roms/nintendo/gamecube/iso\nfi\n"
  },
  {
    "path": "ansible/templates/install_flippydrive/install_flippydrive.sh.j2",
    "content": "#!/bin/bash\n\n# Set world readable/executable umask\numask 0022\n\nSRCDIR=\"/opt/flippydrive\"\ncd \"${SRCDIR}\"\n\nRELEASE=$( curl -kLs https://api.github.com/repos/OffBroadway/flippydrive-assets/releases | jq -r \".[0].assets | map(select(.name | match (\\\"flippy_cli_.*linux\\\")))[-1] | .browser_download_url\" )\n[ -z \"$RELEASE\" ] && echo \"Couldn't get release name name\" && exit 1\n\necho \"Downloading $RELEASE\"\ncd /tmp\ncurl -OJL \"${RELEASE}\"\nunzip flippy_cli_*.zip -d $SRCDIR\n\nrm -f /tmp/flippy_cli_*.zip\n"
  },
  {
    "path": "ansible/templates/install_freestation/retronas_freestation_cifs.conf.j2",
    "content": "## RetroNAS config.\n## This is autogenerated. Changes will be lost.\ncomment = freestation\npath = {{ retronas_path }}/freestation\nguest ok = no\nbrowseable = yes\nwrite list = {{ retronas_user }}\nwriteable = yes\nvalid users = {{ retronas_user }}\ncreate mask = 0775\ndirectory mask = 0775\nfollow symlinks = yes\nwide links = yes\nstrict sync = no\n"
  },
  {
    "path": "ansible/templates/install_freestation/retronas_freestation_nfs.conf.j2",
    "content": "## RetroNAS config.\n## This is autogenerated. Changes will be lost.\n\n/data/retronas/freestation *(rw,async,fsid=75c75da3367aebdeb62b3c9ad16cae53,all_squash,anonuid=1001,anongid=1001,no_subtree_check)"
  },
  {
    "path": "ansible/templates/install_fsp/fspd.conf.j2",
    "content": "## Config autogenerated by RetroNAS\n# FSP is UDP only. Will not conflict with FTP on TCP/21\n# However running on 2121 for unprivileged RetroNAS user\nport 2121\n# Listen on all IPs\nlistenaddress 0.0.0.0\n# MTU 1500 - 40 byte IP/UDP overhead\npacketsize 1460\n# Top level dir for anon connections\nhomedir {{ retronas_path }}/gamecube/swiss\nhomedir_restricted no\ntmpdir tmp\npidlogname /tmp/fspd.pid\ngrabcommand on\nvercommand on\n#dircache 100\n#statcache 30\n#statcache_timeout 20\n#filecache 30\n#setuid {{ retronas_user }}\n#setgid {{ retronas_group }}\numask 0002\n# Don't background. systemd needs this\ndaemonize off\nrestricted off\nreverse_name off\n# Allow writes for save file push\nread_only off\ndebug off\n"
  },
  {
    "path": "ansible/templates/install_fsp/fspd.service.j2",
    "content": "[Unit]\nDescription=FSP File Service Protocol\nDocumentation=http://fsp.sourceforge.net\nRequires=network-online.target\nAfter=network-online.target\n\n[Service]\nUser={{ retronas_user }}\nType=simple\nEnvironment=\"PATH={{ retronas_root }}/bin/fsp:$PATH\"\nExecStart={{ retronas_root }}/bin/fsp/bin/fspd\nTimeoutStopSec=10\nRestart=on-failure\n\n[Install]\nWantedBy=default.target\n"
  },
  {
    "path": "ansible/templates/install_fsp/install_fsp.sh.j2",
    "content": "#!/bin/bash\n\n# Set world readable/executable umask\numask 0022\n\n# Set the install dir\nIDIR=\"{{ retronas_root }}/bin/fsp\"\nmkdir -p \"${IDIR}/etc\"\n\n# make/clean the source build location\nmkdir -p \"{{ retronas_root }}/src\"\ncd \"{{ retronas_root }}/src\"\nrm -rf fsp\n\n# Clone the source code from SourceForge\ngit clone https://git.code.sf.net/p/fsp/code fsp\ncd fsp\n\n# Build and install as per fsp documentation \nscons prefix=\"${IDIR}\" install\n"
  },
  {
    "path": "ansible/templates/install_gogrepo/gogrepo-wrapper.sh.j2",
    "content": "#!/bin/bash\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\n## If this is run as root, switch to our RetroNAS user\n## Manifests and cookies stored in ~/.gogrepo\nCLEAR\n\n## Script Globals\nRNUDIR=$( getent passwd | grep ^${OLDRNUSER}\\: | awk -F ':' '{print $6}' )\nRNUGOGREPO=$RNUDIR/.gogrepo\nGOGREPO=\"/opt/retronas/bin/gogrepo/gogrepo.py\"\nGOGDEPO=\"{{ retronas_path }}/gog\"\nGOGMAN=${RNUDIR}/.gogrepo/gog-manifest.dat\n\nGOGGAME=\"\"\nGOGOS=\"\"\nOSLIST=()\nGAMELIST=()\n\n## New install\n# GOG Operating System\n[ -z \"$OLDGOGOS\" ] && OLDGOGOS=\"none\"\n[ -z \"$OLDGOGLANG\" ] && OLDGOGLANG=\"none\"\nNEWGOGOS=\"windows\"\nNEWGOGLANG=\"en\"\n\n\n## Make the main directory\n[ ! -d $RNUGOGREPO ] && mkdir -p $RNUGOGREPO\ncd $RNUGOGREPO\n\nfunction header {\n    echo \"\"\n    echo \"$1...\"\n    echo \"\"\n}\n\nfunction update {\n    header \"Refreshing GOG manifest...\"\n    DROP_ROOT $GOGREPO update ${@}\n}\n\nfunction login {\n    header \"Initiating GOG login\"\n    header \"Have your GOG username and password ready\"\n    DROP_ROOT $GOGREPO login\n}\n\nfunction download {\n    header \"Downloading/updating games...\"\n    DROP_ROOT $GOGREPO download ${@} \"${GOGDEPO}\"\n}\n\nfunction get_os_list {\n    OS_LIST=(\n        \"windows\"\n        \"mac\"\n        \"linux\"\n        \"windows mac\"\n        \"windows linux\"\n        \"mac linux\"\n        \"windows mac linux\"\n        \"quit\"\n    )\n}\n\nfunction get_lang_list {\n    LANG_LIST=(\n        \"ar\"\n        \"bl\"\n        \"br\"\n        \"cn\"\n        \"cz\"\n        \"da\"\n        \"de\"\n        \"en\"\n        \"es\"\n        \"fi\"\n        \"fr\"\n        \"gk\"\n        \"hu\"\n        \"it\"\n        \"jp\"\n        \"ko\"\n        \"nl\"\n        \"no\"\n        \"pl\"\n        \"pt\"\n        \"ro\"\n        \"ru\"\n        \"sb\"\n        \"sk\"\n        \"sv\"\n        \"tr\"\n        \"quit\"\n    )\n}\n\nfunction get_games_list {\n    GAME_LIST=$(grep \"'title':\" $GOGMAN | awk -F \"'\" '{print $4}' | sort)\n}\n\nfunction select_os {\n    header \"Select the OS you would like to use...\"\n    get_os_list\n\n    PS3=\"Enter a number (current: ${OLDGOGOS}): \"\n    select ITEM in \"${OS_LIST[@]}\"\n    do\n        if [ ! -z \"$ITEM\" ]\n        then\n            if [ \"$ITEM\" != \"quit\" ]\n            then\n                NEWGOGOS=\"$ITEM\"\n            fi\n            break\n        else\n                echo \"Invalid option\"\n        fi\n    done\n\n    if [ ! -z \"$NEWGOGOS\" ]\n    then\n        echo \"Updating GOG settings to $NEWGOGOS\"\n        # update gog setting\n        sed -i '/retronas_gog_os:/d' \"${ANCFG}\"\n        echo \"retronas_gog_os: \\\"${NEWGOGOS}\\\"\" >> \"${ANCFG}\"\n    fi\n}\n\nfunction select_lang {\n    header \"Select the language you would like to use...\"\n    get_lang_list\n\n    PS3=\"Enter a number (current: ${OLDGOGLANG}): \"\n    select KEY in \"${LANG_LIST[@]}\"\n    do\n        if [ ! -z \"${KEY}\" ]\n        then\n            if [ \"${KEY}\" != \"quit\" ]\n            then\n                NEWGOGLANG=\"${KEY}\"\n            fi\n            break\n        else\n            echo \"Invalid option\"\n        fi\n    done\n\n    if [ ! -z \"$NEWGOGLANG\" ]\n    then\n        echo \"Updating GOG settings to $NEWGOGLANG\"\n        # update gog setting\n        sed -i '/retronas_gog_lang:/d' \"${ANCFG}\"\n        echo \"retronas_gog_lang: \\\"${NEWGOGLANG}\\\"\" >> \"${ANCFG}\"\n    fi\n}\n\nfunction gameslist {\n    PS3=\"Enter a number: \"\n    get_games_list\n    select ITEM in $GAME_LIST\n    do\n        if [ ! -z $ITEM ]\n        then\n                GOGGAME=$ITEM\n                break\n        else\n                echo \"Invalid option\"\n        fi\n    done\n}\n\nfunction _usage {\ncat << USAGE\n[ GOGREPO RetroNAS Wrapper ]\n$0 login|import-cookies|select-os|select-lang|update-skip-known|update-download-single|download-all|update-download\n login                      log in to gog\n import-cookies             import a cookies file in LWP/Mozilla formats\n select-os                  select the operating systems you want to target\n select-lang                select the language you want to target\n update-skip-known          update all metadata\n update-download-single     update and download a single title\n download-all               download all know titles\n update-download            update and download all\n\nUSAGE\nexit\n}\n\ncase $1 in \n    login)\n        # login\n        login\n        ;;\n    import-cookies)\n        # import cookies\n        import_cookies\n        ;;\n    update-skip-known)\n        # sync games list\n        update -os ${OLDGOGOS}\n        ;;\n    select-auth)\n        select_auth\n        ;;\n    select-os)\n        select_os\n        ;;\n    select-lang)\n        select_lang\n        ;;\n    update-download-single)\n        # download 1 game\n        gameslist\n        update -os ${OLDGOGOS} -id ${GOGGAME}\n        download -id ${GOGGAME}\n        ;;\n    download-all)\n        # download all games\n        download\n        ;;\n    update-download)\n        # sync and download\n        update -os ${GOGOS}\n        download\n        ;;\n    *)\n        _usage\n      ;;\nesac\n\nPAUSE\n\nexit 0\n"
  },
  {
    "path": "ansible/templates/install_gogrepo/gogrepo_download.sh.j2",
    "content": "#!/bin/bash\necho \"\"\necho \"Downloading/updating games...\"\necho \"\"\n\nmkdir -p ~/.gogrepo 2>/dev/null\ncd ~/.gogrepo\n\n/opt/retronas/bin/gogrepo/gogrepo.py download ${@} \"{{ retronas_path }}/gog\"\n"
  },
  {
    "path": "ansible/templates/install_gogrepo/gogrepo_import-cookies.sh.j2",
    "content": "#!/bin/bash\necho \"\"\necho \"Importing GOG Cookies\"\necho \"\"\n\nset -u\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\n## If this is run as root, switch to our RetroNAS user\n## Manifests and cookies stored in ~/.gogrepo\nCLEAR\n\nGOGREPODIR=/home/{{ retronas_user }}/.gogrepo\n\nFILES=(\n    gog-cookies.dat\n    cookies-gog-com.txt\n)\n\n[ ! -f $GOGREPODIR ] && mkdir -p $GOGREPODIR 2>/dev/null\ncd $GOGREPODIR\n\nfor FILE in ${FILES[@]}\ndo\n    COOKIES=\"${OLDRNPATH}/config/${FILE}\"\n    echo -n \"Looking for $COOKIES\"\n    if [ -f \"${COOKIES}\" ]\n    then\n        DROP_ROOT \\\n          echo \"... Found, importing into ${GOGREPODIR}\"; \\\n          mv -f \"${COOKIES}\" \"${GOGREPODIR}/gog-cookies.dat\"; \\\n          chmod 640 \"${GOGREPODIR}/gog-cookies.dat\"; \\\n          break\n    else\n        echo \"... Not Found\"\n    fi\ndone\n\nchown -R {{ retronas_user }}:{{ retronas_group }} ${GOGREPODIR}\n"
  },
  {
    "path": "ansible/templates/install_gogrepo/gogrepo_login.sh.j2",
    "content": "#!/bin/bash\necho \"\"\necho \"Initiating GOG login\"\necho \"Have your GOG username and password ready\"\necho \"\"\n\nmkdir -p ~/.gogrepo 2>/dev/null\ncd ~/.gogrepo\n\n/opt/retronas/bin/gogrepo/gogrepo.py login\n"
  },
  {
    "path": "ansible/templates/install_gogrepo/gogrepo_update.sh.j2",
    "content": "#!/bin/bash\necho \"\"\necho \"Refreshing GOG manifest...\"\necho \"\"\n\nmkdir -p ~/.gogrepo 2>/dev/null\ncd ~/.gogrepo\n\n/opt/retronas/bin/gogrepo/gogrepo.py update ${@}\n"
  },
  {
    "path": "ansible/templates/install_hb-store-cdn/config.ini.j2",
    "content": "host={{ ansible_default_ipv4.address }}\nport=6449\nbasePath={{ retronas_path }}/ps4/{{ my_name }}\nbinVersion=0.00"
  },
  {
    "path": "ansible/templates/install_hb-store-cdn/hb-store-cdn-check.sh.j2",
    "content": "#!/bin/bash\n\n\nSTATUS=$(sudo -u {{ retronas_user }} {{ retronas_root }}/bin/{{ my_name }}/hb-store-cdn-cli-server check-bin | grep \"Update is required\")\nif [ ! -z \"${STATUS}\" ] \nthen\n    sudo -u {{ retronas_user }} {{ retronas_root }}/bin/{{ my_name }}/hb-store-cdn-cli-server download-bin\n    systemctl restart {{ my_name }}.service\nfi\n"
  },
  {
    "path": "ansible/templates/install_hb-store-cdn/hb-store-cdn.cron.j2",
    "content": "0 */12 * * * {{ retronas_root }}/bin/{{ my_name }}/hb-store-cdn-check.sh"
  },
  {
    "path": "ansible/templates/install_hb-store-cdn/hb-store-cdn.service.j2",
    "content": "[Unit]\nDescription=Homebrew Store for PlayStation4\nAfter=network.target\nStartLimitIntervalSec=60\nStartLimitBurst=4\n\n[Service]\nUser={{ retronas_user }}\nWorkingDirectory={{ retronas_root }}/bin/{{ my_name }}\nExecStart={{ retronas_root }}/bin/{{ my_name }}/hb-store-cdn-cli-server start\nRestart=on-failure\nRestartSec=1\nSuccessExitStatus=3 4\n\nRestartForceExitStatus=3 4\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "ansible/templates/install_hb-store-cdn/install_hb-store-cdn.sh.j2",
    "content": "#!/bin/bash\n\nAPPNAME=\"{{ my_name }}\"\nSRCDIR=\"{{ retronas_root }}/src\"\nBINDIR=\"{{ retronas_root }}/bin/$APPNAME\"\nREPO=https://github.com/Gkiokan/hb-store-cdn-cli-server.git\n\n\n# Use the binary release\n#RELEASE=$( curl -kLs https://api.github.com/repos/Gkiokan/hb-store-cdn-cli-server/releases | jq -r \".[0].assets | map(select(.name | match (\\\"hb-store-cdn-cli-server-linux\\\")))[] | .browser_download_url\" )\n#\n#if [ ! -z \"${RELEASE}\" ]\n#then\n#    DEST=\"${BINDIR}/hb-store-cdn-cli-server\"\n#    curl -JLo\"${DEST}\" \"${RELEASE}\"\n#    chmod +x \"${DEST}\"\n#fi\n\n[ ! -x /usr/bin/npm ] && echo \"npm could not be found\" && exit 1\n\n# max version we can build with is node18\nnpm install -g node@18.20.8\nnpm install -g npm@10.8.2                                                          \n\necho \"Configuring build directories...\"\nrm -rf \"${SRCDIR}\"\nmkdir -p \"${SRCDIR}\"\ncd \"${SRCDIR}\"\n\necho \"Cloning ...\"\ngit clone $REPO $APPNAME\ncd $APPNAME\n\necho \"Install Dependencies\"\n/usr/local/bin/npm install\n/usr/local/bin/npm install pkg -g\n\necho \"Set arch (default x86_64)\"\nMACH=$(uname -m)\ncase $MACH in\n    aarch64|arm*)\n        ARCH=linux-arm\n        ;;\n    *)\n        ARCH=linux\n        ;;\nesac\n\necho \"Building\"\n/usr/local/bin/npm run build:$ARCH\n\nBUILT=release/hb-store-cdn-cli-server\n[ ! -f $BUILT ] && echo \"Build failed\" && exit 1\n\necho \"Moving binary to RetroNAS bin dir...\"\nmkdir -p \"${BINDIR}\" 2>/dev/null\nchmod +x $BUILT\nmv -vf $BUILT \"${BINDIR}\"/\n\necho \"Cleaning up...\"\nrm -rf \"${SRCDIR}\"\n\nchown -R {{ retronas_user }}:{{ retronas_group }}  \"${BINDIR}\"/\necho \"All done!\"\n"
  },
  {
    "path": "ansible/templates/install_hdldump/install_hdldump.sh.j2",
    "content": "#!/bin/bash\n\nset -u\n\nRN_BIN=/usr/local/bin/\n\nAPP=hdl-dump\nREPO=https://github.com/ps2homebrew/${APP}.git\nOUTPATH=/tmp\n\nfunction _log {\n\techo \"$1\"\n}\n\n### REQUIREMENTS\nREQFAIL=0\n#[ ! -x /usr/bin/gmake ] && _log \"GMAKE not found\" && REQFAIL=1\n[ $REQFAIL -ne 0 ] && _log \"Requirements failed, see previous errors\" && exit $REQFAIL\n\n\ncd $OUTPATH\nif [ ! -f ${OUTPATH}/${APP}/.git/config ] \nthen\n\tgit clone $REPO\nelse\n\tcd ${OUTPATH}/${APP}\n\tgit pull\nfi\n\ncd ${OUTPATH}/${APP}\n\nmake RELEASE=yes\n\nmv ${OUTPATH}/${APP}/hdl_dump $RN_BIN/\n\n\n# CLEAN UP\nrm -Rf ${OUTPATH}/${APP}\n"
  },
  {
    "path": "ansible/templates/install_hdparm/hdparm-manager.sh.j2",
    "content": "#!/bin/bash\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\n#CHECK_ROOT\nHDPARM=/usr/sbin/hdparm\nSERVICE=\"hdparm\"\nSELECTION={{ retronas_path }}/config/hdparm-manager-drive\n\n# check for previous selection or default to /dev/sda\nif [ -f $SELECTION ]\nthen\n  DRIVE=$(cat $SELECTION)\nelse\n  DRIVE=/dev/sda\nfi\n\nfunction drive_selector {\n  PS3=\"Select a drive ($DRIVE): \"\n  select DRIVE in $(lsblk --all --list --noheadings | awk '/disk/{print \"/dev/\"$1}') quit\n  do\n    [ $DRIVE == \"quit\" ] && exit\n    echo $DRIVE > $SELECTION\n    exit\n  done\n}\n\ncase $(basename $0 .sh) in\n    hdparm-manager-drive-selector)\n        drive_selector\n    ;;\n    hdparm-manager-disable-apm)\n        sudo ${HDPARM} -B 255 $DRIVE\n    ;;\n    hdparm-manager-disable-standby)\n        sudo ${HDPARM} -S 0 $DRIVE\n    ;;\n    hdparm-manager-start-service)\n      # Start Service\n        sudo systemctl reset-failed ${SERVICE}.service\n        sudo systemctl restart ${SERVICE}.timer\n        sudo systemctl enable ${SERVICE}.timer\n        sudo systemctl restart ${SERVICE}.service\n    ;;\n    hdparm-manager-query-service)\n      # Start Service\n        sudo systemctl status ${SERVICE}.timer\n        sudo systemctl status ${SERVICE}.service\n    ;;\n    hdparm-manager-stop-service)\n      # Start Service\n        sudo systemctl stop ${SERVICE}.timer\n        sudo systemctl stop ${SERVICE}.service\n\n        sudo systemctl disable ${SERVICE}.timer\n        sudo systemctl disable ${SERVICE}.service\n    ;;\n    *)\n    exit 1\n    ;;\nesac"
  },
  {
    "path": "ansible/templates/install_hdparm/hdparm.service.j2",
    "content": "﻿[Unit]\nDescription=HDPARM HDD No Sleep\n\n[Service]\nType=simple\nExecStart={{ retronas_root }}/scripts/hdparm.sh\nTimeoutStopSec=10\nRestart=no\n\n[Install]\nWantedBy=default.target\n"
  },
  {
    "path": "ansible/templates/install_hdparm/hdparm.sh.j2",
    "content": "#!/bin/bash\n#\n# Prevent a disk going to sleep by reading a sector\n#\n\nTRIES=0\nRANDSECTOR=1\n\nerror() {\n  >&2 echo \"$1\"\n}\n\nlog() {\n  echo \"$1\"\n}\n\n# check for config file\n[ ! -f {{ retronas_path }}/config/hdparm-manager-drive ] && error \"Failed to find config file\" && exit 1\n\nfunction set_sector() {\n  local MAXSECTOR=$1\n  RANDSECTOR=$(shuf -i 1-${MAXSECTOR} -n 1 2>&1)\n\n\n  # test to see if the above was successful, can fail on systems without lfs compiled\n  # see https://www.gnu.org/software/coreutils/faq/coreutils-faq.html\n       \n  if [ $? -ne 0 ]\n  then\n    error \"Failed to determine randsec at $MAXSECTOR ($TRIES/$RETRY)\"\n    [ $TRIES -eq 3 ] && RANDSECTOR=1 && exit\n    MAXSECTOR=$((${MAXSECTOR}/2))\n    TRIES=$((${TRIES}+1))\n    set_sector $MAXSECTOR\n  fi\n\n}\n\n# list the drives that need to be punished\nNOSLEEP=(\n  $(cat {{ retronas_path }}/config/hdparm-manager-drive)\n)\n\n# REQUIREMENTS\nREQFAIL=0\n[ $REQFAIL -ne 0 ] && error \"Failed requiements, check previous messages\" && exit $REQFAIL\n\nfor DISK in ${NOSLEEP[@]}\ndo\n        [ ! -r $DISK ] && error \"$DISK is not readable, exiting\" && exit 1\n        BLOCKDEV=$(echo $DISK | sed 's/\\/dev\\///')\n        MAXSECTOR=$(cat /sys/class/block/${BLOCKDEV}/size)\n        [ -z $MAXSECTOR ] && error \"Cannot obtain MAXSECTORS from $DISK, exiting\" && exit 1\n        \n        set_sector $MAXSECTOR\n\n        if [ ! -z \"${RANDSECTOR}\" ]\n        then\n          log \"Reading ${RANDSECTOR} (max:${MAXSECTOR}) from ${DISK}\"\n          hdparm --read-sector $RANDSECTOR $DISK &>/dev/null\n        else\n          error \"Failed to obtain a random sector is the drive available?\" \n        fi\ndone\n"
  },
  {
    "path": "ansible/templates/install_hdparm/hdparm.timer.j2",
    "content": "﻿[Unit]\nDescription=Run HDPARM HDD No Sleep\n\n[Timer]\nOnBootSec=3m\nOnUnitActiveSec=3m\nRandomizedDelaySec=2m\n\n[Install]\nWantedBy=timers.target\n"
  },
  {
    "path": "ansible/templates/install_hfsutils/install_hfsutils.sh.j2",
    "content": "#!/bin/bash\n\nset -u\n\n## we are using a fork because trixie doesn't have a package\n# see: https://tracker.debian.org/pkg/hfsutils\n\nREPO=hfsutils\nOWNER=jepler\nBRANCH=misc-fixes\nSRCDIR=\"{{ retronas_root }}/src\"\n\necho \"Configuring build directories...\"\nrm -rf \"${SRCDIR}\"\nmkdir -p \"${SRCDIR}\"\ncd \"${SRCDIR}\"\n\necho \"Downloading hfsutils source code...\"\ngit clone https://github.com/$OWNER/$REPO\ncd $REPO\n[ ! -z $BRANCH ] && git checkout $BRANCH\n\necho \"Building libhfs\"\n## libhfs\ncd libhfs\nautoconf\n./configure\nmake\n\ncd ..\necho \"Building hfsutils\"\n## hfsutils\nautoconf\n./configure\nmake\n\necho \"Installing hfsutils\"\nmake install\n\n\necho \"Cleaning up\"\nrm -rf $SRCDIR\n"
  },
  {
    "path": "ansible/templates/install_hostapd/hostapd-dnsmasq.conf.j2",
    "content": "dhcp-range={{ retronas_net_wifi_interface }},{{ retronas_net_wifi_dhcprange }}\ndhcp-option={{ retronas_net_wifi_interface }},option:router,{{ retronas_net_wifi_router }}\ndhcp-option={{ retronas_net_wifi_interface }},option:ntp-server,{{ retronas_net_wifi_ntp }}\n#dhcp-option={{ retronas_net_wifi_interface }},19,0 # ip-forwarding off\n"
  },
  {
    "path": "ansible/templates/install_hostapd/hostapd-retronas.conf.j2",
    "content": "interface={{ retronas_net_wifi_interface }}\nssid={{ retronas_net_wifi_ssid }}\nchannel={{ retronas_net_wifi_channel }}\nhw_mode={{ retronas_net_wifi_hwmode }}\ncountry_code={{ retronas_net_wifi_countrycode }}\nauth_algs=1\nignore_broadcast_ssid=0\nwpa=2\nwpa_key_mgmt=WPA-PSK\nwpa_pairwise=TKIP CCMP\nrsn_pairwise=CCMP\nwpa_passphrase={{ retronas_wifi_password.stdout }}\nieee80211d=1\nieee80211n=1\nctrl_interface=/var/run/hostapd\nctrl_interface_group=0\n\n# attempts to fix disassociation of c64 wifi\ndisassoc_low_ack=0 \nbeacon_int=100\ndtim_period=2\nwpa_ptk_rekey=600\n"
  },
  {
    "path": "ansible/templates/install_hostapd/hostapd-retronas.service.j2",
    "content": "[Unit]\nDescription=Access point and authentication server for retronas\nDocumentation=man:hostapd(8)\nRequires=network.target\nAfter=network.target\nStartLimitIntervalSec=2\nStartLimitBurst=5\nBindsTo=sys-subsystem-net-devices-{{ retronas_net_wifi_interface }}.device\nAfter=sys-subsystem-net-devices-{{ retronas_net_wifi_interface }}.device\n\n[Service]\nType=forking\nExecStart=/usr/sbin/hostapd -B -P /run/hostapd-retronas.pid /etc/hostapd/hostapd-retronas.conf\nRestartSec=10s\nTimeoutStartSec=8s\nRestart=on-failure\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "ansible/templates/install_kermit/iksd.socket.j2",
    "content": "[Socket]\nListenStream = 1649\nAccept=yes\n\n[Install]\nWantedBy = sockets.target"
  },
  {
    "path": "ansible/templates/install_kermit/iksd@.service.j2",
    "content": "[Unit]\nDescription=Internet Kermit Server\n\n[Service]\n# Note the - to make systemd ignore the exit code\nExecStart=-/usr/sbin/iksd -A --dbfile:/var/run/iksd/iksd.db --syslog:5 --root:{{ retronas_path }} --anonymous:on\n\n# This is the part that makes it work like inetd\nStandardInput=socket\nStandardOutput=socket\n\nDynamicUser=no\nUser=root\nGroup=root\n\n# /usr, /boot, /etc read-only\nProtectSystem=strict\nProtectHome=true\nNoNewPrivileges=true\nRuntimeDirectory=iksd\nReadWritePaths=/var/run/iksd /run/iksd /var/log\nReadOnlyPaths={{ retronas_path }}\n\n# We can't establish new network connections\nRestrictAddressFamilies=AF_INET AF_INET6 AF_PACKET\n\n[Install]\nWantedBy=multi-user.target"
  },
  {
    "path": "ansible/templates/install_lighttpd/99-retronas.conf.j2",
    "content": "server.document-root  = \"{{ retronas_path }}\"\nserver.username       = \"{{ retronas_user }}\"\nserver.groupname      = \"{{ retronas_group }}\"\ndir-listing.activate  = \"enable\"\n"
  },
  {
    "path": "ansible/templates/install_linux-dexdrive/dexdrive_dumper.sh.j2",
    "content": "#!/bin/bash\n\nset -u\n\nDEXDEV=/dev/dexdrive0\nDESYS=${1:-ps1}\n\n# PS1\nDEXEXT=\"mcd\"\nDEXPATH={{ retronas_path }}/saves/sony/playstation1\ncase $DESYS in\n    n64)\n        DEXEXT=\"n64\"\n        DEXPATH={{ retronas_path }}/saves/nintendo/nintendo64\n        echo \"N64 is untested, let me know what raw save format is for N64\"\n        PAUSE\n        exit 0\n        ;;\n    *)\n        ;;\nesac\n\nFILENAME=\"\"\nwhile [ -z \"${FILENAME}\" ]\ndo\n    read -p \"Please type a name for the memcard to be saved as (no extension, (q)uit): \" FILENAME\n    case \"${FILENAME}\" in\n        Q|q)\n            echo \"Quitting\"\n            exit 1\n            ;;\n        *)\n            echo \"${FILENAME}\"\n    esac\ndone\n\nif [ -b ${DEXDEV} ]\nthen\n    DUMPFILE=\"${DEXPATH}/${FILENAME}.${DEXEXT}\"\n    echo \"Dumping ${DEXDEV} to ${DUMPFILE}, please wait ...\"\n    dd if=\"${DEXDEV}\" of=\"${DUMPFILE}\"\n    chown {{ retronas_user }}:{{ retronas_group }} \"${DUMPFILE}\"\nelse\n    echo \"$DEXDEV not found, didn't you connect it?\"\n    sleep 3\nfi"
  },
  {
    "path": "ansible/templates/install_linux-dexdrive/install_linux-dexdrive.sh.j2",
    "content": "#!/bin/bash\n\nset -u\n\nAPP=linux-dexdrive\nREPO=https://github.com/fbriere/${APP}.git\nOUTPATH=$(mktemp -d)\n\nfunction _log {\n\techo \"$1\"\n}\n\n\nHEADERS=linux-headers-$(uname -r)\ngrep \"Raspberry Pi\" /proc/device-tree/model\n[ $? -eq 0 ] && HEADERS=raspberrypi-kernel-headers\n\n# install the required source files\napt-get -y install git build-essential $HEADERS\n\ncd $OUTPATH\nif [ ! -f ${OUTPATH}/${APP}/.git/config ] \nthen\n\tgit clone $REPO\nelse\n\tcd ${OUTPATH}/${APP}\n\tgit pull\nfi\n\ncd ${OUTPATH}/${APP}\ngit apply /tmp/makefile.patch\nmake\nmake install\n\necho \"dexdrive\" >> /etc/modprobe.d/dexdrive.conf\n\n# remove the required source files\napt-get -y remove $HEADERS"
  },
  {
    "path": "ansible/templates/install_linux-dexdrive/linux-dexdrive.service.j2",
    "content": "﻿[Unit]\nDescription=Dex Drive service\nConditionPathExists=/dev/ttyUSB0\n\n[Service]\nType=forking\nExecStart=/usr/local/bin/dexattach -D /dev/ttyUSB0\nTimeoutStopSec=10\nRestart=no\n\n[Install]\nWantedBy=default.target\n"
  },
  {
    "path": "ansible/templates/install_linux-dexdrive/makefile.patch.j2",
    "content": "diff --git a/Makefile b/Makefile\nindex 7e5a5f0..622f248 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -27,6 +27,7 @@ modules:\n install: all\n \tcp dexattach $(DESTDIR)/$(PREFIX)/bin\n \t$(MAKE) -C $(KERNELDIR) M=$(PWD) INSTALL_MOD_PATH=$(abspath $(DESTDIR)) modules_install\n+\tdepmod -A\n \n clean:\n \trm -rf dexattach\n"
  },
  {
    "path": "ansible/templates/install_linux-gadgets/gadget-mass-storage-manage.sh.j2",
    "content": "#!/bin/bash\n\nMODULE=g_mass_storage\nDEVICE=\"\"\nDEVICEPATH=\"\"\nSYSTEMD=\"retronas-gadget\"\n\nselect_device() {\n    IFS=$'\\n'\n    DEVICE=()\n    DEVICES+=($(lsblk -sl 2>/dev/null | awk '/(disk|part|rom)/{print $1\" (\"$4\")\"}' | sort -u))\n    DEVICES+=($(ls -la {{ retronas_path }}/images/* 2>/dev/null | awk '{print $9}'))\n    DEVICES+=(\"quit\")\n\n    [ {{ ''.join (('$','{', '#DEVICES[@]}')) }} -le 1 ] && echo \"No targets found, exiting ... \" && exit 1\n    select SDEVICE in \"${DEVICES[@]}\"\n    do\n        DEVICE=\"$(echo ${SDEVICE} | awk -F ' ' '{print $1}')\"\n        [ $SDEVICE == \"quit\" ] && echo \"User requested exit ...\" && exit 0\n        [ ! -z \"${DEVICE}\" ] && break\n    done\n}\n\ncreate_systemd_path() {\n    DEVICEPATH=$(ls -la /dev/disk/by-path/ | grep $DEVICE | head -n1 | awk '{print $9}')\n\n    if [ ! -z \"${DEVICEPATH}\" ]\n    then\n    echo \"Configuring systemd path file for ${DEVICE} (${DEVICEPATH})\"\n\ncat << PEOF > /etc/systemd/system/${SYSTEMD}.path\n[Path]\nPathExists=/dev/disk/by-path/$DEVICEPATH\n\n[Install]\nWantedBy=remote-fs.target\nPEOF\n\n    fi\n\n}\n\ncreate_systemd_service() {\n    DEVICEPATH=$(ls -la /dev/disk/by-path/ | grep $DEVICE | head -n1 | awk '{print $9}')\n\n    if [ ! -z \"${DEVICEPATH}\" ]\n    then\n    echo \"Configuring systemd service file for ${DEVICE} (${DEVICEPATH})\"\n\ncat << SEOF > /etc/systemd/system/${SYSTEMD}.service\n[Unit]\nDescription=Gadget Device\nRequires=open-iscsi.service\n\n[Service]\nType=oneshot\nExecStartPre=-echo on > /sys/bus/usb/devices/usb1/power/control\nExecStartPre=-/usr/sbin/rmmod g_mass_storage\nExecStartPre=/usr/bin/sleep 2\nExecStart=/usr/sbin/modprobe g_mass_storage file=/dev/disk/by-path/${DEVICEPATH} stall=0 ro=0 removable=1\nRestart=no\nRemainAfterExit=yes\nSEOF\n\n    fi\n\n}\n\nenable_systemd() {\n    systemctl daemon-reload\n    systemctl enable ${SYSTEMD}.path\n    systemctl start ${SYSTEMD}.path\n}\n\nselect_device\ncreate_systemd_path\ncreate_systemd_service\n#enable_systemd"
  },
  {
    "path": "ansible/templates/install_litch/litch_claim.sh.j2",
    "content": "#!/bin/bash\necho \"\"\necho \"litch claim bundles...\"\necho \"\"\n\nmkdir -p ~/.litch 2>/dev/null\ncd ~/.litch\n\n/opt/retronas/bin/litch/litch.py --claim-bundles\n"
  },
  {
    "path": "ansible/templates/install_litch/litch_download.sh.j2",
    "content": "#!/bin/bash\necho \"\"\necho \"litch download purchases ...\"\necho \"\"\n\nmkdir -p ~/.litch 2>/dev/null\ncd ~/.litch\n\n/opt/retronas/bin/litch/litch.py --download-purchases --content-path {{ retronas_path }}/itchio\n"
  },
  {
    "path": "ansible/templates/install_litch/litch_download_clean.sh.j2",
    "content": "#!/bin/bash\necho \"\"\necho \"litch download purchases ...\"\necho \"\"\n\nmkdir -p ~/.litch 2>/dev/null\ncd ~/.litch\n\n/opt/retronas/bin/litch/litch.py --download-purchases --cleanup-incorrect-files --content-path {{ retronas_path }}/itchio\n"
  },
  {
    "path": "ansible/templates/install_litch/litch_login.sh.j2",
    "content": "#!/bin/bash\necho \"\"\necho \"litch login ...\"\necho \"\"\n\nmkdir -p ~/.litch 2>/dev/null\ncd ~/.litch\n\n/opt/retronas/bin/litch/litch.py --login\n"
  },
  {
    "path": "ansible/templates/install_macproxy_classic/macproxy.service.j2",
    "content": "[Unit]\nDescription=Macproxy Classic service\nAfter=network.target\n\n[Service]\nType=simple\nRestart=always\nExecStart=/opt/macproxy_classic/start_macproxy.sh\nSyslogIdentifier=MACPROXY\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "ansible/templates/install_megatools/install_megatools.sh.j2",
    "content": "#!/bin/bash\n\nset -u\n\nRN_BIN=/usr/local/bin/\n\nAPP=megatools\nREPO=https://xff.cz/git/${APP}\nOUTPATH=/tmp\n\nfunction _log {\n\techo \"$1\"\n}\n\n### REQUIREMENTS\nREQFAIL=0\n#[ ! -x /usr/bin/make ] && _log \"MAKE not found\" && REQFAIL=1\n[ $REQFAIL -ne 0 ] && _log \"Requirements failed, see previous errors\" && exit $REQFAIL\n\n\ncd $OUTPATH\nif [ ! -f ${OUTPATH}/${APP}/.git/config ] \nthen\n\tgit clone $REPO\nelse\n\tcd ${OUTPATH}/${APP}\n\tgit pull\nfi\n\ncd ${OUTPATH}/${APP}\n\n# patch for old debian glib\n#sed -i 's/g_memdup2/g_memdup/g' lib/mega.c\n#\n\nmeson setup build\nninja -C build\nninja -C build install\n\n#mv ${OUTPATH}/${APP}/build/megatools $RN_BIN/\n\n# CLEAN UP\nrm -Rf ${OUTPATH}/${APP}\n"
  },
  {
    "path": "ansible/templates/install_minicom/minicom.sh.j2",
    "content": "#!/bin/bash\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\ncase $(basename $0 .sh) in\n    minicom-config)\n        DROP_ROOT /usr/bin/minicom -s\n        [ $? -ne 0 ] && PAUSE\n    ;;\n    *)\n        DROP_ROOT /usr/bin/minicom\n        [ $? -ne 0 ] && PAUSE\n    ;;\nesac\n"
  },
  {
    "path": "ansible/templates/install_minicom/minirc.dfl.j2",
    "content": "# Machine-generated file - use \"minicom -s\" to change parameters.\npu updir            {{ retronas_path }}/{{ my_name }}\npu downdir          {{ retronas_path }}/{{ my_name }}\n"
  },
  {
    "path": "ansible/templates/install_mister-organize/install_mister-organize.sh.j2",
    "content": "#!/bin/bash\n\nset -ue\n\nROMVAULT=3.7.4\nMISTORG=\"{{ retronas_path }}/{{ module_name }}\"\n\ncd \"${MISTORG}\"\n\ncase $(uname -m) in\n  x86_64)\n    ARCH=x64\n    ;;\n  *)\n    echo \"Unsupported Architecture\"\n    ;;\nesac\n\nDIRS=(\n DatRoot\n RomRoot\n)\n\n# Create RomVault Directories\nfor DIR in ${DIRS[@]}\ndo\n [ ! -d $DIR ] && mkdir $DIR\ndone\nln -sfT ../romimport ToSort\n\n# Setup RomVault Config\nRVNAME=\"RomVault Basic Setup\"\nif [ -f \"${RVNAME}.zip\" ]\nthen\n  unzip -jo \"${RVNAME}.zip\" \"${RVNAME}/RomVault3cfg.xml\"\nfi\n\n# Move Dat files\nrm -f \"${MISTORG}/DatRoot/MiSTer_*.dat\"\nfind $MISTORG -maxdepth 1 -name \"*.dat\" -exec mv \"{}\" DatRoot/ \\;\n# Unpack the console dat file that is zipped\nfind $MISTORG -maxdepth 1 -name \"MiSTer_Console*.zip\" -exec unzip -jo -d DatRoot/ {} \\;\n\n# Clean up\ncd $MISTORG\nrm -f *.zip\n\n# Obtain RomVault for Linux\nRVZIP=RVCmd_V${ROMVAULT}-Linux-${ARCH}.zip\ncd /tmp\ncurl -skLO https://www.romvault.com/download/${RVZIP}\nif [ -f ${RVZIP} ]\nthen\n  unzip -o ${RVZIP} -d $MISTORG\n  chmod +x ${MISTORG}/RomVaultCmd\n  rm -f $RVZIP\nelse\n  echo \"Failed to download\"\n  exit 1\nfi\n\nchown -R {{ retronas_user }}:{{ retronas_group }} {{ retronas_path }}/{{ module_name }}\n"
  },
  {
    "path": "ansible/templates/install_mister-organize/mister-organize.sh.j2",
    "content": "#!/bin/bash\n\ncd {{ retronas_path }}/{{ module_name }}/\nsu -p retronas -c \"./RomVaultCmd -all\"\n"
  },
  {
    "path": "ansible/templates/install_mister_cifs/retronas-mister-dirs.service.j2",
    "content": "[Unit]\nDescription=retronas-mister-dirs\nAfter=multi-user.target\n\n[Service]\nType=simple\nRestart=no\nWorkingDirectory=/opt/retronas/ansible\nExecStartPre=/usr/bin/git reset --hard HEAD\nExecStartPre=/usr/bin/git pull\nExecStart=/usr/bin/ansible-playbook --extra-vars \"dirs_only=true\" {{ my_file }}.yml\nTimeoutStartSec=0\nRemainAfterExit=no\n\n[Install]\nWantedBy=multi-user.target"
  },
  {
    "path": "ansible/templates/install_mister_cifs/retronas-mister-dirs.timer.j2",
    "content": "[Unit]\nDescription=Run retronas-mister-dirs\n\n[Timer]\nOnCalendar=daily\nPersistent=true\n\n[Install]\nWantedBy=timers.target"
  },
  {
    "path": "ansible/templates/install_mtcp-netdrive/install_mtcp-netdrive.sh.j2",
    "content": "#!/bin/bash\n\nVERSION=${1:-2025-01-10}\n\nARCBIN=https://www.brutman.com/mTCP/download/mTCP_NetDrive_server-bin_${VERSION}.zip\nDEST=/opt/mtcp-netdrive\nLZIP=mtcp.zip\n\n# download\nif [ ! -f ${TMPDIR}/${LZIP} ]\nthen\n\tcurl -JLo${TMPDIR}/${LZIP} \"${ARCBIN}\"\nfi\n\n# extract\nif [ -f ${TMPDIR}/${LZIP} ]\nthen\n    unzip -j -d $DEST ${TMPDIR}/${LZIP}\nfi\n\ncase $(uname -m) in\n\taarch64)\n\t\tBIN=netdrive_linux_arm64\n\t;;\n\tx86_64)\n\t\tBIN=netdrive_linux_amd64\n\t;;\n\t*)\n\techo \"Unknown architecture $(uname -m)\"\nesac\n\nif [ -f \"${DEST}/${BIN}\" ]\nthen\n    chmod +x \"${DEST}/${BIN}\"\n    ln -sf \"${DEST}/${BIN}\"  \"${DEST}/netdrive\"\nfi\n\nchown -R {{ retronas_user }}:{{ retronas_group }} $DEST"
  },
  {
    "path": "ansible/templates/install_mtcp-netdrive/mtcp-netdrive.service.j2",
    "content": "[Unit]\nDescription=mTCP Netdrive\nRequires=network.target\nAfter=network.target\n\n[Service]\nType=forking\nWorkingDirectory=/opt/mtcp-netdrive\nExecStart={{ retronas_root }}/scripts/mtcp-netdrive.sh start\nExecStop={{ retronas_root }}/scripts/mtcp-netdrive.sh stop\n# only because somewhere in the chain screen decides to report failure\nSuccessExitStatus=1\nRestart=no\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "ansible/templates/install_mtcp-netdrive/mtcp-netdrive.sh.j2",
    "content": "#!/bin/bash\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\nMODE=${1:-config}\nSESSION=mtcp-netdrive\n\n[ ! -f /opt/mtcp-netdrive/netdrive ] && echo \"mTCP Netdrive is not installed\" && exit 1\n\ncd /opt/mtcp-netdrive/\n\ncase $MODE in\n    console)\n        DROP_ROOT /usr/bin/screen -x $SESSION\n        ;;\n    start)\n        DROP_ROOT /usr/bin/screen -dmS $SESSION ./netdrive -log_file /opt/mtcp-netdrive/log.txt serve -image_dir /data/retronas/roms/microsoft/dos/\n        ;;\n    stop)\n        DROP_ROOT /usr/bin/screen -S $SESSION -X stuff \"quit^M\"\n        ;;\n    *)\n        exit\n        ;;\nesac\n"
  },
  {
    "path": "ansible/templates/install_mtcp-netdrive/mtcp-netdrive.xml.j2",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<service>\n  <port port=\"2002\" protocol=\"udp\"/>\n</service>"
  },
  {
    "path": "ansible/templates/install_mysticbbs/create_filebone_na.py.j2",
    "content": "#!/usr/bin/env python3\n# \n# sairuk, built with limited scope for RetroNAS deduping filearea creation\n#\n## references\n# https://git.shipoclu.com/moon/enigma-bbs/src/commit/60369ea378be82cd76ba954577b9bd34dec919a3/core/oputil/oputil_file_base.js\n# https://web.archive.org/web/20060507083451/http://www.filegate.net/info/filegate.zxx\n# https://talk.dallasmakerspace.org/t/bbs-utils-reading-mystic-database-files/56722\n#\n## based on \n# - lastcallers.py : (CC No-By)2019-2034 Dallas Makerspace, Some Rights Reserved : Authors: Dwight Spencer (denzuko) <denzuko@dallasmakerspace.org>\n# - mystic_library.php : netsurge : mysphp14.zip\n\nimport os, yaml\nfrom struct import Struct\n\nRN_SYSTEMS=\"{{ retronas_root }}/ansible/retronas_systems.yml\"\nRN_BASE=\"{{ retronas_path }}/roms/\"\n\n# NOTE: this is not a comprehensive intepretation of the data, just enough for what we need\nfbase_t = Struct(\"I 60s ? 60s ? 61s ? 175s ? 61s ? 80s ? 8s ? ? 123s\")\n\nBYTES=640\n\nignored = [\n    'system_map', \n    'system_links',\n    \"system_template\", \n]\n\ndef yaml_data():\n    data = []\n    ind =  open(RN_SYSTEMS,'r').read()\n    odata = yaml.safe_load(ind)\n    for key in odata.keys():\n        if key not in ignored:\n            for item in odata[key]:\n                if item[\"pretty_name\"] != \"\":\n                    data.append({\"src\": item[\"src\"], \"pretty_name\": item[\"pretty_name\"]})\n    return data\n\n\ndef filebone(data={}, fa={}):\n    # output the filebone.na format\n    print(\"% File Echo                   Description\")\n    print(\"%\")\n    for item in data:\n        d = item[\"src\"]\n        n = item[\"pretty_name\"]\n        ad = os.path.join(RN_BASE,d)\n        if ad not in fa.values():\n            print(f\"Area {d}  0     !      RTNS: {n}\")\n    print(\"%\")\n    print(\"% End of RetroNAS Fileecho List\")\n\ndef fileareas(dat=\"fbases.dat\"):\n\n    dat = os.path.join(\"data\",dat)\n\n    if not os.path.exists(dat):\n        return []\n\n    fileareas = {}\n\n    filesize = os.stat(dat).st_size\n    records = int(filesize / BYTES)\n\n    with open(dat, 'rb') as data:\n        by = data.read()\n        ba = [by[i:i+BYTES] for i in range(-1, len(by), BYTES)]\n\n    for record in range(1, records):\n        filearea = fbase_t.unpack_from(ba[record])\n        fname = filearea[3].decode().strip('\\x00')[1:]\n        fname = fname.split(\":\")[-1].replace(\"_\", \" \").lstrip()\n        fpath = filearea[11].decode().strip('\\x00')[:-1]\n        fileareas[fname] = fpath\n\n    return fileareas\n\ndef main(dat=\"fbases.dat\"):\n\n    fa = fileareas()\n    data = yaml_data()\n    filebone(data, fa)\n\n\nif __name__ == \"__main__\":\n\tmain()\n"
  },
  {
    "path": "ansible/templates/install_mysticbbs/install_mysticbbs.sh.j2",
    "content": "#!/bin/bash\n\nDEST=/opt/mysticbbs\nTMPDEST=/tmp/mysticbbs\nDESTFILE=mysticbbs\nDL=\"\"\nEXT=\"\"\n\n# version locked because auto upgrade cannot be tested atm\n# `install auto overwrite` overwrites key configuration, like filebases, general config etc\n\nif [ -f /opt/mysticbbs/mystic.dat ]\nthen\n\techo \"Upgrading Mystic is a manual process once installed, exiting otherwise your bbs could be wiped\"\n\texit\nfi\n\ncase $(uname -m) in\n\taarch64)\n\t\tDL=https://www.mysticbbs.com/downloads/mys112a48_p64.zip\n\t;;\n\tx86_64)\n\t\tDL=https://www.mysticbbs.com/downloads/mys112a48_l64.rar\n\t;;\n\t*)\n\techo \"Unknown architecture $(uname -m)\"\nesac\n\n\nif [ ! -z \"${DL}\" ]\nthen\n\tEXT=\"${DL##*.}\"\n\tDESTFILE=\"${DESTFILE}.${EXT}\"\n\tif [ ! -f $TMPDIR/$DESTFILE ]\n\tthen\n\t\tcurl -JLo $TMPDIR/$DESTFILE \"${DL}\"\n\tfi\nfi\n\n[ ! -d $DEST ] && mkdir -p $DEST\n\nif [ -f $TMPDIR/$DESTFILE ]\nthen\n\t7z x -o${TMPDEST} -y \"${TMPDIR}/${DESTFILE}\"\nfi\n\nchown -R retronas:retronas $DEST\nchmod +x $TMPDEST/install $TMPDEST/upgrade\n\ncd $TMPDEST\n./install auto $DEST overwrite\nrm -f $TMPDIR/$DESTFILE\n\nif [ -f /opt/mysticbbs/mutil ]\nthen\n\t{{ retronas_root }}/scripts/mysticbbs.sh filearea\n\t{{ retronas_root }}/scripts/mysticbbs.sh upload\nfi\n"
  },
  {
    "path": "ansible/templates/install_mysticbbs/mysticbbs-mis.service.j2",
    "content": "[Unit]\nDescription=MysticBBS Internet Server\nRequires=network.target\nAfter=network.target\n\n[Service]\nType=forking\nWorkingDirectory=/opt/mysticbbs\nExecStart=/opt/mysticbbs/mis daemon\nExecStop=/opt/mysticbbs/mis shutdown\nRestartSec=10s\nTimeoutStartSec=8s\nRestart=on-failure\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "ansible/templates/install_mysticbbs/mysticbbs.sh.j2",
    "content": "#!/bin/sh\n\n\nMODE=${1:-config}\nFA_CREATED=retronas.filesareas_done\n\n[ ! -f /opt/mysticbbs/mystic ] && echo \"Mystic is not installed\" && exit 1\n\ncd /opt/mysticbbs\n\ncase $MODE in\n    local)\n        ./mystic -l\n       ;;\n    filearea)\n        #if [ -f $FA_CREATED ]\n        #then\n        #    echo \"File areas for RetroNAS have already been imported, running this again will result in duplicate fileareas\"\n        #    echo \"If you are sure, remove the file $FA_CREATED and run this option again\"\n        #    exit\n        #fi\n\n        if [ -f retronas_create_fileareas.ini ]\n        then\n            [ -f retronas.na ] && mv retronas.na retronas.na.$(date +'%s')\n            python3 create_filebone_na.py > retronas.na\n            ./mutil retronas_create_fileareas.ini\n            rm retronas.na\n            touch $FA_CREATED\n        fi\n        ;;\n    upload)\n        ./mutil retronas_massupload.ini\n        ;;\n    *)\n        ./mystic -cfg\n        ;;\nesac"
  },
  {
    "path": "ansible/templates/install_mysticbbs/mysticbbs.xml.j2",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<service>\n  <port port=\"23\" protocol=\"tcp\"/>\n  <port port=\"513\" protocol=\"tcp\"/>\n  <port port=\"22\" protocol=\"tcp\"/>\n  <port port=\"24554\" protocol=\"tcp\"/>\n  <port port=\"21\" protocol=\"tcp\"/>\n  <port port=\"119\" protocol=\"tcp\"/>\n  <port port=\"110\" protocol=\"tcp\"/>\n  <port port=\"25\" protocol=\"tcp\"/>\n</service>"
  },
  {
    "path": "ansible/templates/install_mysticbbs/retronas_create_fileareas.ini.j2",
    "content": "[General]\n        Import_FILEBONE.NA = true\n\n[Import_FILEBONE.NA]\n\n        ; filename of filebone.na\n\n        filename = retronas.na\n\n        ; root directory to create file paths under.  when a new filebone echo\n        ; is found, mUtil will create a file using \"root_dir\" + areatag.  So\n        ; for example if root is \"c:\\mystic\\filebase\\\" and the areatag is\n        ; MYSTICBBS it will create \"c:\\mystic\\filebase\\MYSTICBBS\".  This root\n        ; directory MUST exist.\n\n        root_dir = /data/retronas/roms\n\n        ; Use echotag for base description and FTP name\n\n        use_echotag = false\n\n        ; Convert tags to lower case for filebase base filename/dir\n        ; True or 1 for yes, false or 0 for no\n\n        lowercase_filename = false\n\n        ; Default values when creating a new file base\n\n        dispfile     =\n        template     = ansiflst\n        acs_list     =\n        acs_ftp      =\n        acs_download =\n        acs_upload   =\n        acs_hatch    =\n        acs_sysop    = s255\n\n        ; true/false type values 0=false 1=true (newscan 2=forced)\n        ; true/false type values 0=false 1=true (newscan 2=forced)\n\n        new_scan      = 1\n        free_files    = 0\n        show_uploader = 1\n        anon_ftp      = 0\n"
  },
  {
    "path": "ansible/templates/install_mysticbbs/retronas_massupload.ini.j2",
    "content": "[General]\n        MassUpload         = true\n\n[MassUpload]\n\n        ; this function searches all configured file directories for new\n        ; files and will upload them into the BBS.  It will attempt to\n        ; import FILE_ID.DIZ using the configured archivers if the option\n        ; is enabled.\n\n        ; Name to save has the uploader\n\n        uploader_name = RetroNAS\n\n        ; Import FILE_ID.DIZ?  1=yes\n\n        import_fileid = 1\n\n        ; Rename filenames that are longer than the maximum allowed length\n        length_rename = false\n\n        ; No description string used when no FILE_ID.DIZ is imported.\n\n        no_description = No Description\n\n        ; Ignore list one file mask per line (allows * and ? wildcards)\n\n        ignore = files.bbs\n        ;ignore = *.readme"
  },
  {
    "path": "ansible/templates/install_nabu/install_nabu.sh.j2",
    "content": "#!/bin/bash\n\nTMPFILES=$(mktemp -d)\n\ndeclare -A VERSIONS=(\n    ['x86_64']='x64'\n    ['aarch64']='arm64'\n    ['arm']='arm'\n)   \n\nBINDIR=/opt/nabu\nARCH=$(uname -m)\nARCHIVE=linux-${VERSIONS[$ARCH]}.zip\n\n[ ! -d $BINDIR ] && mkdir -p $BINDIR\n\ncd $TMPFILES\ncurl -O http://cloud.nabu.ca/${ARCHIVE}\n\nif [ -f ${ARCHIVE} ]\nthen\n\n     unzip -j $ARCHIVE -d $BINDIR\n     rm -f $ARCHIVE\n     chmod +x $BINDIR/NABU-Internet-Adapter*\n     chown -R {{ retronas_user }}:{{ retronas_group }} $BINDIR\n\nfi\n"
  },
  {
    "path": "ansible/templates/install_nabu/nabu.sh.j2",
    "content": "#!/bin/bash\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\nBINDIR=/opt/nabu\nBIN=NABU-Internet-Adapter-84\n\ncd $BINDIR\n\nLIB=libdl.so\n[ ! -f ${LIB} ] && ln -s /lib/x86_64-linux-gnu/${LIB}.2 ${LIB}\n\nDROP_ROOT \"./$BIN\"\n"
  },
  {
    "path": "ansible/templates/install_nbd-client/nbd.conf.j2",
    "content": "nbd"
  },
  {
    "path": "ansible/templates/install_netatalk2/AppleVolumes.default.j2",
    "content": "# Netatalk 2.x afp volume cofiguration\n\n#\n# volume format:\n# :DEFAULT: [all of the default options except volume name]\n# path [name] [casefold:x] [options:z,l,j] \\\n#   [allow:a,@b,c,d] [deny:a,@b,c,d] [dbpath:path] [password:p] \\\n#   [rwlist:a,@b,c,d] [rolist:a,@b,c,d] [limitsize:value in bytes] \\\n#   [preexec:cmd] [root_preexec:cmd] [postexec:cmd]  [root_postexec:cmd] \\\n#   [allowed_hosts:IPv4 address[/IPv4 netmask bits]] \\\n#   [denied_hosts:IPv4 address[/IPv4 netmask bits]] \\\n#   ... more, see below ...\n#   \n# name:      volume name. it can't include the ':' character\n#\n\n#\n# variable substitutions:\n# you can use variables for both <path> and <name> now. here are the\n# rules:\n#     1) if you specify an unknown variable, it will not get converted. \n#     2) if you specify a known variable, but that variable doesn't have\n#        a value, it will get ignored.\n#\n# the variables:\n# $b   -> basename of path\n# $c   -> client's ip or appletalk address\n# $d   -> volume pathname on server    \n# $f   -> full name (whatever's in the gecos field)\n# $g   -> group\n# $h   -> hostname \n# $i   -> client ip without tcp port or appletalk network   \n# $s   -> server name (can be the hostname)\n# $u   -> username (if guest, it's whatever user guest is running as)\n# $v   -> volume name (either ADEID_NAME or basename of path)\n# $z   -> zone (may not exist)\n# $$   -> $\n#\n\n#\n# casefold options [syntax: casefold:option]:\n# tolower    -> lowercases names in both directions\n# toupper    -> uppercases names in both directions\n# xlatelower -> client sees lowercase, server sees uppercase\n# xlateupper -> client sees uppercase, server sees lowercase\n#\n# allow/deny/rwlist/rolist format [syntax: allow:user1,@group]:\n# user1,@group,user2  -> allows/denies access from listed users/groups\n#                        rwlist/rolist control whether or not the\n#                        volume is ro for those users.\n# allowed_hosts       -> Only listed hosts and networks are allowed,\n#                        all others are rejected. Example:\n#                        allowed_hosts:10.1.0.0/16,10.2.1.100\n# denied_hosts        -> Listed hosts and nets are rejected,\n#                        all others are allowed. Example:\n#                        denied_hosts: 192.168.100/24,10.1.1.1\n# preexec             -> command to be run when the volume is mounted,\n#                        ignore for user defined volumes\n# root_preexec        -> command to be run as root when the volume is mounted,\n#                        ignore for user defined volumes\n# postexec            -> command to be run when the volume is closed,\n#                        ignore for user defined volumes\n# root_postexec       -> command to be run as root when the volume is closed,\n#                        ignore for user defined volumes\n# veto                -> hide files and directories,where the path matches\n#                        one of the \"/\" delimited vetoed names. Matches are\n#                        partial, e.g. path is /abc/def/file and veto:/abc/\n#                        will hide the file.\n# adouble             -> specify the format of the metadata files.\n#                        default is \"v2\". netatalk 1.x used \"v1\".\n#                        \"osx\" cannot be treated normally any longer.\n# volsizelimit        -> size in MiB.  Useful for TimeMachine: limits the\n#                         reported volume size, thus preventing TM from using\n#                         the whole real disk space for backup.\n#                         Example: \"volsizelimit:1000\" would limit the\n#                         reported disk space to 1 GB.\n\n\n#\n# codepage options [syntax: options:charsetname]\n# volcharset          -> specifies the charset to be used\n#                        as the volume codepage\n#                        e.g. \"UTF8\", \"UTF8-MAC\", \"ISO-8859-15\"\n# maccharset          -> specifies the charset to be used\n#                        as the legacy client (<=Mac OS 9) codepage\n#                        e.g. \"MAC_ROMAN\", \"MAC_CYRILLIC\"\n#\n# perm                -> default permission value\n#                        OR with the client requested perm\n#                        Use with options:upriv\n# dperm               -> default permission value for directories\n#                        OR with the client requested perm\n#                        Use with options:upriv\n# fperm               -> default permission value for files\n#                        OR with the client requested perm\n#                        Use with options:upriv\n# umask               -> set perm mask\n#                        Use with options:upriv\n# dbpath:path         -> store the database stuff in the following path.\n# cnidserver:server[:port]\n#                     -> Query this servername or IP address\n#                        (default:localhost) and port (default: 4700)\n#                        for CNIDs. Only used with CNID backend \"dbd\".\n#                        This option here overrides any setting from\n#                        afpd.conf:cnidserver.\n# password:password   -> set a volume password (8 characters max)\n# cnidscheme:scheme   -> set the cnid scheme for the volume,\n#                        default is [dbd]\n#                        available schemes: [dbd last tdb]\n# ea                  -> none|auto|sys|ad\n#                        Specify how Extended Attributes are stores. default\n#                        is auto.\n#                        auto: try \"sys\" (by setting an EA on the shared\n#                              directory itself), fallback to \"ad\".  Requires\n#                              writable volume for performing the test.\n#                              Note: options:ro overwrites \"auto\" with \"none.\"\n#                        sys:  Use filesystem EAs\n#                        ad:   Use files in AppleDouble directories\n#                        none: No EA support\n#\n\n#\n# miscellaneous options [syntax: options:option1,option2]:\n# tm                  -> enable TimeMachine support\n# prodos              -> make compatible with appleII clients.\n# crlf                -> enable crlf translation for TEXT files.\n# noadouble           -> don't create .AppleDouble unless a resource\n#                        fork needs to be created.\n# ro                  -> mount the volume as read-only.\n# mswindows           -> enforce filename restrictions imposed by MS\n#                        Windows. this will also invoke a default\n#                        codepage (iso8859-1) if one isn't already \n#                        specified.\n# nohex               -> don't do :hex translations for anything\n#                        except dot files. specify usedots as well if\n#                        you want that turned off. note: this option\n#                         makes the / character illegal.\n# usedots             -> don't do :hex translation for dot files. note: when \n#                        this option gets set, certain file names\n#                        become illegal. these are .Parent and\n#                        anything that starts with .Apple.\n# invisibledots       -> don't do :hex translation for dot files. note: when \n#                        this option gets set, certain file names\n#                        become illegal. these are .Parent and\n#                        anything that starts with .Apple. also, dot\n#                        files created on the unix side are marked invisible. \n# limitsize           -> limit disk size reporting to 2GB. this is\n#                        here for older macintoshes using newer\n#                        appleshare clients. yucko.\n# nofileid            -> don't advertise createfileid, resolveid, deleteid \n#                        calls\n# root_preexec_close  -> a non-zero return code from root_preexec close the \n#                        volume being mounted.\n# preexec_close       -> a non-zero return code from preexec close the \n#                        volume being mounted.\n# nostat              -> don't stat volume path when enumerating volumes list\n# upriv               -> use unix privilege.  \n# illegalseq          -> encode illegal sequence in filename asis,\n#                        ex \"\\217-\", which is not a valid SHIFT-JIS char,\n#                        is encoded  as U\\217 -\n# nocnidcache         -> Don't store and read CNID to/from AppleDouble file.\n#                        This should not be used as it also prevents a CNID\n#                        database rebuild with `dbd`!\n# caseinsensitive     -> The underlying FS is case insensitive (only \n#                        test with JFS in OS2 mode)\n# dropbox             -> Allows a volume to be declared as being a \"dropbox.\"\n#                        Note that netatalk must be compiled with dropkludge\n#                        support for this to function. Warning: This option\n#                        is deprecated and might not work as expected.\n# dropkludge          -> same as \"dropbox\"\n# nodev               -> always use 0 for device number, helps when the\n#                        device number is not constant across a reboot,\n#                        cluster, ...\n#\n\n# The line below sets some DEFAULT, starting with Netatalk 2.1.\n:DEFAULT: options:upriv,usedots\n\n# By default all users have access to their home directories.\n{{ retronas_path }}\t\t\t\"retronas\"\n\n# End of File\n"
  },
  {
    "path": "ansible/templates/install_netatalk2/afpd.conf.j2",
    "content": "#\n# CONFIGURATION FOR AFPD (Netatalk 2.x)\n#\n# Each single line defines a virtual server that should be available.\n# Though, using \"\\\" character, newline escaping is supported.\n# Empty lines and lines beginning with `#' are ignored.\n# Options in this file will override both compiled-in defaults\n# and command line options.\n#\n\n\n#\n# Format:\n#  - [options]               to specify options for the default server\n#  \"Server name\" [options]   to specify an additional server\n#\n\n\n#\n# The following options are available:\n#   Transport Protocols:\n#     -[no]tcp       Make \"AFP over TCP\" [not] available\n#     -[no]ddp       Make \"AFP over AppleTalk\" [not] available.\n#                    If you have -proxy specified, specify -uamlist \"\" to \n#                    prevent ddp connections from working.\n#\n#     -transall      Make both available\n#\n#   Transport Options:\n#     -ipaddr <ipaddress> Specifies the IP address that the server should\n#                         advertise and listens to. The default is advertise\n#                         the first IP address of the system, but to listen\n#                         for any incoming request. The network address may\n#                         be specified either in dotted-decimal format for\n#                         IPv4 or in hexadecimal format for IPv6.\n#                         This option also allows to use one machine to\n#                         advertise the AFP-over-TCP/IP settings of another\n#                         machine via NBP when used together with the -proxy\n#                         option.\n#     -server_quantum <number> \n#                         Specifies the DSI server quantum. The minimum\n#                         value is 1MB. The max value is 0xFFFFFFFF. If you \n#                         specify a value that is out of range, you'll get \n#                         the default value (currently the minimum).\n#     -admingroup <groupname>\n#                         Specifies the group of administrators who should\n#                         all be seen as the superuser when they log in.\n#                         Default is disabled.\n#     -ddpaddr x.y        Specifies the DDP address of the server.\n#                         the  default is to auto-assign an address (0.0).\n#                         this is only useful if you're running on\n#                         a multihomed host.\n#     -port <number>      Specifies the TCP port the server should respond\n#                         to (default is 548)\n#     -fqdn <name:port>   specify a fully-qualified domain name (+optional\n#                         port). this gets discarded if the server can't\n#                         resolve it. this is not honored by appleshare\n#                         clients <= 3.8.3 (default: none)\n#     -hostname <name>    Use this instead of the result from calling\n#                         hostname for dertermening which IP address to\n#                         advertise, therfore the hostname is resolved to\n#                         an IP which is the advertised. This is NOT used for\n#                         listening and it is also overwritten by -ipaddr.\n#     -proxy              Run an AppleTalk proxy server for specified\n#                         AFP/TCP server (if address/port aren't given,\n#                         then first IP address of the system/548 will\n#                         be used).\n#                         if you don't want the proxy server to act as\n#                         a ddp server as well, set -uamlist to an empty\n#                         string.\n#     -dsireadbuf [number]\n#                         Scale factor that determines the size of the\n#                         DSI/TCP readahead buffer, default is 12. This is\n#                         multiplies with the DSI server quantum (default\n#                         ~300k) to give the size of the buffer. Increasing\n#                         this value might increase throughput in fast local\n#                         networks for volume to volume copies.  Note: This\n#                         buffer is allocated per afpd child process, so\n#                         specifying large values will eat up large amount of\n#                         memory (buffer size * number of clients).\n#     -tcprcvbuf [number]\n#                         Try to set TCP receive buffer using setsockpt().\n#                         Often OSes impose restrictions on the applications\n#                         ability to set this value.\n#     -tcpsndbuf [number]\n#                         Try to set TCP send buffer using setsockpt().\n#                         Often OSes impose restrictions on the applications\n#                         ability to set this value.\n#     -slp                Register this server with the Service Location\n#                         Protocol (if SLP support was compiled in).\n#     -nozeroconf         Don't register this server with the Multicats\n#                         DNS Protocol.\n#     -advertise_ssh      Allows Mac OS X clients (10.3.3-10.4) to\n#                         automagically establish a tunneled AFP connection\n#                         through SSH. This option is not so significant\n#                         for the recent Mac OS X. See the Netatalk Manual\n#                         in detail.\n#\n#\n#   Authentication Methods:\n#     -uampath <path>  Use this path to look for User Authentication Modules.\n#                      (default: /usr/lib/netatalk)\n#     -uamlist <a,b,c> Comma-separated list of UAMs.\n#                      (default: uams_dhx.so,uams_dhx2.so)\n#\n#                      some commonly available UAMs:\n#                      uams_guest.so: Allow guest logins\n#\n#                      uams_clrtxt.so: (uams_pam.so or uams_passwd.so)\n#                                     Allow logins with passwords\n#                                     transmitted in the clear. \n#\n#                      uams_randnum.so: Allow Random Number and Two-Way\n#                                      Random Number exchange for\n#                                      authentication.\n#\n#                      uams_dhx.so: (uams_dhx_pam.so or uams_dhx_passwd.so)\n#                                  Allow Diffie-Hellman eXchange\n#                                  (DHX) for authentication.\n#\n#                      uams_dhx2.so: (uams_dhx2_pam.so or uams_dhx2_passwd.so)\n#                                   Allow Diffie-Hellman eXchange 2\n#                                   (DHX2) for authentication.\n#\n#   Password Options:\n#     -[no]savepassword   [Don't] Allow clients to save password locally\n#     -passwdfile <path>  Use this path to store Randnum passwords.\n#                         (Default: /etc/netatalk/afppasswd. The only other\n#                         useful value is ~/.passwd. See 'man afppasswd'\n#                         for details.)\n#     -passwdminlen <#>   minimum password length. may be ignored.\n#     -[no]setpassword    [Don't] Allow clients to change their passwords.\n#     -loginmaxfail <#>   maximum number of failed logins. this may be\n#                         ignored if the uam can't handle it.\n#\n#   AppleVolumes files:\n#     -defaultvol <path>  Specifies path to AppleVolumes.default file\n#                         (default /etc/netatalk/AppleVolumes.default,\n#                         same as -f on command line)\n#     -systemvol <path>   Specifies path to AppleVolumes.system file\n#                         (default /etc/netatalk/AppleVolumes.system,\n#                         same as -s on command line)\n#     -[no]uservolfirst   [Don't] read the user's ~/AppleVolumes or\n#                         ~/.AppleVolumes before reading\n#                         /etc/netatalk/AppleVolumes.default\n#                         (same as -u on command line)\n#     -[no]uservol        [Don't] Read the user's volume file\n#     -closevol           Immediately unmount volumes removed from\n#                         AppleVolumes files on SIGHUP sent to the afp\n#                         master process.\n#\n#   Miscellaneous:\n#     -authprintdir <path> Specifies the path to be used (per server) to \n#                          store the files required to do CAP-style\n#                          print authentication which papd will examine\n#                          to determine if a print job should be allowed.\n#                          These files are created at login and if they\n#                          are to be properly removed, this directory\n#                          probably needs to be umode 1777\n#     -guestname \"user\"   Specifies the user name for the guest login\n#                         (default \"nobody\", same as -g on command line)\n#     -loginmesg \"Message\"  Client will display \"Message\" upon logging in\n#                         (no default, same as -l \"Message\" on commandline)\n#     -nodebug            Switch off debugging\n#     -client_polling     With this switch enabled, afpd won't advertise\n#                         that it is capable of server notifications, so that\n#                         connected clients poll the server every 10 seconds\n#                         to detect changes in opened server windows.\n#                         Note: Depending on the number of simultaneously\n#                         connected clients and the network's speed, this can\n#                         lead to a significant higher load on your network!\n#     -sleep   <number>   AFP 3.x wait number hours before disconnecting\n#                         clients in sleep mode. Default 10 hours\n#     -tickleval <number> Specify the tickle timeout interval (in seconds).\n#                         Note, this defaults to 30 seconds, and really \n#                         shouldn't be changed.  If you want to control\n#                         the server idle timeout, use the -timeout option.\n#     -timeout <number>   Specify the number of tickles to send before\n#                         timing out a connection.\n#                         The default is 4, therefore a connection will\n#                         timeout in 2 minutes.\n#     -[no]icon           [Don't] Use the platform-specific icon. Recent\n#                         Mac OS don't display it any longer.\n#     -volnamelen <number>\n#                         Max length of UTF8-MAC volume name for Mac OS X.\n#                         Note that Hangul is especially sensitive to this.\n#                           255: limit of spec\n#                           80:  limit of generic Mac OS X (default)\n#                           73:  limit of Mac OS X 10.1, if >= 74\n#                                Finder crashed and restart repeatedly.\n#                         Mac OS 9 and earlier is not influenced by this,\n#                         Maccharset volume names are always limitted to 27.\n#     -[un]setuplog \"<logtype> <loglevel> [<filename>]\"\n#                         Specify that any message of a loglevel up to the\n#                         given loglevel should be logged to the given file.\n#                         If the filename is ommited the loglevel applies to\n#                         messages passed to syslog.\n#\n#                         By default (no explicit -setuplog and no buildtime\n#                         configure flag --with-logfile) afpd logs to syslog\n#                         with a default logging setup equivalent to\n#                         \"-setuplog default log_info\".\n#\n#                         If build with --with-logfile[=somefile]\n#                         (default logfile /var/log/netatalk.log) afpd\n#                         defaults to a setup that is equivalent to\n#                         \"-setuplog default log_info [netatalk.log|somefile]\"\n#\n#                         logtypes:  Default, AFPDaemon, Logger, UAMSDaemon\n#                         loglevels: LOG_SEVERE, LOG_ERROR, LOG_WARN,\n#                                    LOG_NOTE, LOG_INFO, LOG_DEBUG,\n#                                    LOG_DEBUG6, LOG_DEBUG7, LOG_DEBUG8,\n#                                    LOG_DEBUG9, LOG_MAXDEBUG\n#\n#                Example: Useful default config\n#                         -setuplog \"default log_info /var/log/afpd.log\"\n#\n#                         Debugging config\n#                         -setuplog \"default log_maxdebug /var/log/afpd.log\"\n#\n#     -signature { user:<text> | auto }\n#                         Specify a server signature. This option is useful\n#                         while running multiple independent instances of\n#                         afpd on one machine (e.g. in clustered environments,\n#                         to provide fault isolation etc.).\n#                         Default is \"auto\".\n#                         \"auto\" signature type allows afpd generating\n#                         signature and saving it to afp_signature.conf\n#                         automatically (based on random number).\n#                         \"host\" signature type switches back to \"auto\"\n#                         because it is obsoleted.\n#                         \"user\" signature type allows administrator to\n#                         set up a signature string manually.\n#                         Examples: three servers running on one machine:\n#                               first   -signature user:USERS\n#                               second  -signature user:USERS\n#                               third   -signature user:ADMINS\n#                         First two servers will act as one logical AFP\n#                         service. If user logs in to first one and then\n#                         connects to second one, session will be\n#                         automatically redirected to the first one. But if\n#                         client connects to first and then to third, \n#                         will be asked for password twice and will see\n#                         resources of both servers.\n#                         Traditional method of signature generation causes\n#                         two independent afpd instances to have the same\n#                         signature and thus cause clients to be redirected\n#                         automatically to server (s)he logged in first.\n#     -k5keytab <path>\n#     -k5service <service>\n#     -k5realm <realm>\n#                         These are required if the server supports\n#                         Kerberos 5 authentication\n#     -ntdomain\n#     -ntseparator\n#                         Use for e.g. winbind authentication, prepends\n#                         both strings before the username from login and\n#                         then tries to authenticate with the result\n#                         through the available and active UAM authentication\n#                         modules.\n#     -dircachesize entries\n#                         Maximum possible entries in the directory cache.\n#                         The cache stores directories and files. It is used\n#                         to cache the full path to directories and CNIDs\n#                         which considerably speeds up directory enumeration.\n#                         Default size is 8192, maximum size is 131072. Given\n#                         value is rounded up to nearest power of 2. Each\n#                         entry takes about 100 bytes, which is not much, but\n#                         remember that every afpd child process for every\n#                         connected user has its cache.\n#     -fcelistener host[:port]\n#                         Enables sending FCE events to the specified host,\n#                         default port is 12250 if not specified. Specifying\n#                         mutliple listeners is done by having this option\n#                         once for each of them.\n#     -fceevents fmod,fdel,ddel,fcre,dcre,tmsz\n#                         Speficies which FCE events are active, default is\n#                         fmod,fdel,ddel,fcre,dcre.\n#     -fcecoalesce all|delete|create\n#                         Coalesce FCE events.\n#     -fceholdfmod seconds\n#                         This determines the time delay in seconds which is\n#                         always waited if another file modification for the\n#                         same file is done by a client before sending an FCE\n#                         file modification event (fmod). For example saving\n#                         a file in Photoshop would generate multiple events\n#                         by itself because the application is opening,\n#                         modifying and closing a file mutliple times for\n#                         every \"save\". Defautl: 60 seconds.\n#     -keepsessions       Enable \"Continuous AFP Service\". This means the\n#                         ability to stop the master afpd process with a\n#                         SIGQUIT signal, possibly install an afpd update and\n#                         start the afpd process. Existing AFP sessions afpd\n#                         processes will remain unaffected. Technically they\n#                         will be notified of the master afpd shutdown, sleep\n#                         15-20 seconds and then try to reconnect their IPC\n#                         channel to the master afpd process. If this\n#                         reconnect fails, the sessions are in an undefined\n#                         state. Therefor it's absolutely critical to restart\n#                         the master process in time!\n#     -noacl2maccess      Don't map filesystem ACLs to effective permissions.\n#\n#   Codepage Options:\n#     -unixcodepage <CODEPAGE>  Specifies the servers unix codepage,\n#                               e.g. \"ISO-8859-15\" or \"UTF8\".\n#                               This is used to convert strings to/from\n#                               the systems locale, e.g. for authenthication.\n#                               Defaults to LOCALE if your system supports it,\n#                               otherwise ASCII will be used.\n#\n#     -maccodepage <CODEPAGE>   Specifies the legacy clients (<= Mac OS 9)\n#                               codepage, e.g. \"MAC_ROMAN\".\n#                               This is used to convert strings to the\n#                               systems locale, e.g. for authenthication\n#                               and SIGUSR2 messaging. This will also be\n#                               the default for volumes maccharset.\n#\n#   CNID related options:\n#     -cnidserver <ipaddress:port>\n#                               Specifies the IP address and port of a\n#                               cnid_metad server, required for CNID dbd\n#                               backend. Defaults to localhost:4700.\n#                               The network address may be specified either\n#                               in dotted-decimal format for IPv4 or in\n#                               hexadecimal format for IPv6.\n#\n#   Avahi (Bonjour) related options:\n#     -mimicmodel <model>\n#                               Specifies the icon model that appears on\n#                               clients. Defaults to off. Examples: RackMac\n#                               (same as Xserve), PowerBook, PowerMac, Macmini,\n#                               iMac, MacBook, MacBookPro, MacBookAir, MacPro,\n#                               AppleTV1,1, AirPort\n#\n\n\n#\n# Some examples:\n#\n#       The simplest case is to not have an afpd.conf.\n#\n#       4 servers w/ names server1-3 and one w/ the hostname. servers\n#       1-3 get routed to different ports with server 3 being bound \n#       specifically to address 192.168.1.3\n#\n#           -\n#           server1 -port 12000\n#           server2 -port 12001\n#           server3 -port 12002 -ipaddr 192.168.1.3\n#\n#       a dedicated guest server, a user server, and a special\n#       AppleTalk-only server:\n#\n#           \"Guest Server\" -uamlist uams_guest.so \\\n#                   -loginmesg \"Welcome guest! I'm a public server.\"\n#           \"User Server\" -uamlist uams_dhx2.so -port 12000\n#           \"special\" -ddp -notcp -defaultvol <path> -systemvol <path>\n#\n\n\n# default:\n# - -tcp -noddp -uamlist uams_dhx.so,uams_dhx2.so\n- -transall -icon -mimicmodel PowerMac\n"
  },
  {
    "path": "ansible/templates/install_netatalk2/default.j2",
    "content": "# Netatalk 2.x configuration\n\n#########################################################################\n# Global configuration\n#########################################################################\n\n#### machine's AFPserver/AppleTalk name.\nATALK_NAME=retroafp\n\n#### server (unix) and legacy client (<= Mac OS 9) charsets\nATALK_UNIX_CHARSET='LOCALE'\nATALK_MAC_CHARSET='MAC_ROMAN'\n\n#### Don't Edit. export the charsets, read form ENV by apps\nexport ATALK_UNIX_CHARSET\nexport ATALK_MAC_CHARSET\n\n#########################################################################\n# AFP specific configuration\n#########################################################################\n\n#### Set which daemons to run.\n#### If you use AFP file server, run both cnid_metad and afpd.\nCNID_METAD_RUN=yes\nAFPD_RUN=yes\n\n#### maximum number of clients that can connect:\n#AFPD_MAX_CLIENTS=20\n\n#### UAMs (User Authentication Modules)\n#### available options: uams_dhx.so, uams_dhx2.so, uams_guest.so,\n####                    uams_clrtxt.so(legacy), uams_randnum.so(legacy)\nAFPD_UAMLIST=\"-U uams_dhx2.so,uams_clrtxt.so,uams_dhx.so,uams_guest.so,uams_randnum.so\"\n\n#### Set the id of the guest user when using uams_guest.so\n#AFPD_GUEST=nobody\n\n#### config for cnid_metad. Default log config:\n#CNID_CONFIG=\"-l log_note\"\n\n#########################################################################\n# AppleTalk specific configuration (legacy)\n#########################################################################\n\n#### Set which legacy daemons to run.\n#### If you need AppleTalk, run atalkd.\n#### papd, timelord and a2boot are dependent upon atalkd.\nATALKD_RUN=yes\nPAPD_RUN=yes\nTIMELORD_RUN=yes\nA2BOOT_RUN=yes\n\n#### Control whether the daemons are started in the background.\n#### If it is dissatisfied that legacy atalkd starts slowly, set \"yes\".\n#### In case using systemd/systemctl, this is not so significant.\nATALK_BGROUND=yes\n\n#### Set the AppleTalk Zone name.\n#### NOTE: if your zone has spaces in it, you're better off specifying\n####       it in atalkd.conf\n#ATALK_ZONE=@zone\n"
  },
  {
    "path": "ansible/templates/install_netatalk2x/AppleVolumes.default.j2",
    "content": "# Netatalk 2.x afp volume cofiguration\n# This file looks empty when viewed with \"vi\".  In fact, there is one\n# '~', so users with no AppleVolumes file in their home directory get\n# their home directory by default.\n\n#\n# volume format:\n# :DEFAULT: [all of the default options except volume name]\n# path [name] [casefold:x] [options:z,l,j] \\\n#   [allow:a,@b,c,d] [deny:a,@b,c,d] [dbpath:path] [password:p] \\\n#   [rwlist:a,@b,c,d] [rolist:a,@b,c,d] [limitsize:value in bytes] \\\n#   [preexec:cmd] [root_preexec:cmd] [postexec:cmd]  [root_postexec:cmd] \\\n#   [allowed_hosts:IPv4 address[/IPv4 netmask bits]] \\\n#   [denied_hosts:IPv4 address[/IPv4 netmask bits]] \\\n#   ... more, see below ...\n#   \n# name:      volume name. it can't include the ':' character\n#\n\n#\n# variable substitutions:\n# you can use variables for both <path> and <name> now. here are the\n# rules:\n#     1) if you specify an unknown variable, it will not get converted. \n#     2) if you specify a known variable, but that variable doesn't have\n#        a value, it will get ignored.\n#\n# the variables:\n# $b   -> basename of path\n# $c   -> client's ip or appletalk address\n# $d   -> volume pathname on server    \n# $f   -> full name (whatever's in the gecos field)\n# $g   -> group\n# $h   -> hostname \n# $i   -> client ip without tcp port or appletalk network   \n# $s   -> server name (can be the hostname)\n# $u   -> username (if guest, it's whatever user guest is running as)\n# $v   -> volume name (either ADEID_NAME or basename of path)\n# $z   -> zone (may not exist)\n# $$   -> $\n#\n\n#\n# casefold options [syntax: casefold:option]:\n# tolower    -> lowercases names in both directions\n# toupper    -> uppercases names in both directions\n# xlatelower -> client sees lowercase, server sees uppercase\n# xlateupper -> client sees uppercase, server sees lowercase\n#\n# allow/deny/rwlist/rolist format [syntax: allow:user1,@group]:\n# user1,@group,user2  -> allows/denies access from listed users/groups\n#                        rwlist/rolist control whether or not the\n#                        volume is ro for those users.\n# allowed_hosts       -> Only listed hosts and networks are allowed,\n#                        all others are rejected. Example:\n#                        allowed_hosts:10.1.0.0/16,10.2.1.100\n# denied_hosts        -> Listed hosts and nets are rejected,\n#                        all others are allowed. Example:\n#                        denied_hosts: 192.168.100/24,10.1.1.1\n# preexec             -> command to be run when the volume is mounted,\n#                        ignore for user defined volumes\n# root_preexec        -> command to be run as root when the volume is mounted,\n#                        ignore for user defined volumes\n# postexec            -> command to be run when the volume is closed,\n#                        ignore for user defined volumes\n# root_postexec       -> command to be run as root when the volume is closed,\n#                        ignore for user defined volumes\n# veto                -> hide files and directories,where the path matches\n#                        one of the \"/\" delimited vetoed names. Matches are\n#                        partial, e.g. path is /abc/def/file and veto:/abc/\n#                        will hide the file.\n# adouble             -> specify the format of the metadata files.\n#                        default is \"v2\". netatalk 1.x used \"v1\".\n#                        \"osx\" cannot be treated normally any longer.\n# volsizelimit        -> size in MiB.  Useful for TimeMachine: limits the\n#                         reported volume size, thus preventing TM from using\n#                         the whole real disk space for backup.\n#                         Example: \"volsizelimit:1000\" would limit the\n#                         reported disk space to 1 GB.\n\n\n#\n# codepage options [syntax: options:charsetname]\n# volcharset          -> specifies the charset to be used\n#                        as the volume codepage\n#                        e.g. \"UTF8\", \"UTF8-MAC\", \"ISO-8859-15\"\n# maccharset          -> specifies the charset to be used\n#                        as the legacy client (<=Mac OS 9) codepage\n#                        e.g. \"MAC_ROMAN\", \"MAC_CYRILLIC\"\n#\n# perm                -> default permission value\n#                        OR with the client requested perm\n#                        Use with options:upriv\n# dperm               -> default permission value for directories\n#                        OR with the client requested perm\n#                        Use with options:upriv\n# fperm               -> default permission value for files\n#                        OR with the client requested perm\n#                        Use with options:upriv\n# umask               -> set perm mask\n#                        Use with options:upriv\n# dbpath:path         -> store the database stuff in the following path.\n# cnidserver:server[:port]\n#                     -> Query this servername or IP address\n#                        (default:localhost) and port (default: 4700)\n#                        for CNIDs. Only used with CNID backend \"dbd\".\n#                        This option here overrides any setting from\n#                        afpd.conf:cnidserver.\n# password:password   -> set a volume password (8 characters max)\n# cnidscheme:scheme   -> set the cnid scheme for the volume,\n#                        default is [dbd]\n#                        available schemes: [dbd last tdb]\n# ea                  -> none|auto|sys|ad\n#                        Specify how Extended Attributes are stores. default\n#                        is auto.\n#                        auto: try \"sys\" (by setting an EA on the shared\n#                              directory itself), fallback to \"ad\".  Requires\n#                              writable volume for performing the test.\n#                              Note: options:ro overwrites \"auto\" with \"none.\"\n#                        sys:  Use filesystem EAs\n#                        ad:   Use files in AppleDouble directories\n#                        none: No EA support\n#\n\n#\n# miscellaneous options [syntax: options:option1,option2]:\n# tm                  -> enable TimeMachine support\n# prodos              -> make compatible with appleII clients.\n# crlf                -> enable crlf translation for TEXT files.\n# noadouble           -> don't create .AppleDouble unless a resource\n#                        fork needs to be created.\n# ro                  -> mount the volume as read-only.\n# mswindows           -> enforce filename restrictions imposed by MS\n#                        Windows. this will also invoke a default\n#                        codepage (iso8859-1) if one isn't already \n#                        specified.\n# nohex               -> don't do :hex translations for anything\n#                        except dot files. specify usedots as well if\n#                        you want that turned off. note: this option\n#                         makes the / character illegal.\n# usedots             -> don't do :hex translation for dot files. note: when \n#                        this option gets set, certain file names\n#                        become illegal. these are .Parent and\n#                        anything that starts with .Apple.\n# invisibledots       -> don't do :hex translation for dot files. note: when \n#                        this option gets set, certain file names\n#                        become illegal. these are .Parent and\n#                        anything that starts with .Apple. also, dot\n#                        files created on the unix side are marked invisible. \n# limitsize           -> limit disk size reporting to 2GB. this is\n#                        here for older macintoshes using newer\n#                        appleshare clients. yucko.\n# nofileid            -> don't advertise createfileid, resolveid, deleteid \n#                        calls\n# root_preexec_close  -> a non-zero return code from root_preexec close the \n#                        volume being mounted.\n# preexec_close       -> a non-zero return code from preexec close the \n#                        volume being mounted.\n# nostat              -> don't stat volume path when enumerating volumes list\n# upriv               -> use unix privilege.  \n# illegalseq          -> encode illegal sequence in filename asis,\n#                        ex \"\\217-\", which is not a valid SHIFT-JIS char,\n#                        is encoded  as U\\217 -\n# nocnidcache         -> Don't store and read CNID to/from AppleDouble file.\n#                        This should not be used as it also prevents a CNID\n#                        database rebuild with `dbd`!\n# caseinsensitive     -> The underlying FS is case insensitive (only \n#                        test with JFS in OS2 mode)\n# dropbox             -> Allows a volume to be declared as being a \"dropbox.\"\n#                        Note that netatalk must be compiled with dropkludge\n#                        support for this to function. Warning: This option\n#                        is deprecated and might not work as expected.\n# dropkludge          -> same as \"dropbox\"\n# nodev               -> always use 0 for device number, helps when the\n#                        device number is not constant across a reboot,\n#                        cluster, ...\n#\n\n# The line below sets some DEFAULT, starting with Netatalk 2.1.\n:DEFAULT: options:upriv,usedots\n\n# The \"~\" below indicates that Home directories are visible by default.\n# If you do not wish to have people accessing their Home directories,\n# please put a pound sign in front of the tilde or delete it.\n#~\n\n{{ retronas_path }}\tretroafp\toptions:prodos,upriv,usedots\n\n# End of File\n"
  },
  {
    "path": "ansible/templates/install_netatalk2x/afpd.conf.j2",
    "content": "#\n# CONFIGURATION FOR AFPD (Netatalk 2.x)\n#\n# Each single line defines a virtual server that should be available.\n# Though, using \"\\\" character, newline escaping is supported.\n# Empty lines and lines beginning with `#' are ignored.\n# Options in this file will override both compiled-in defaults\n# and command line options.\n#\n\n\n#\n# Format:\n#  - [options]               to specify options for the default server\n#  \"Server name\" [options]   to specify an additional server\n#\n\n\n#\n# The following options are available:\n#   Transport Protocols:\n#     -[no]tcp       Make \"AFP over TCP\" [not] available\n#     -[no]ddp       Make \"AFP over AppleTalk\" [not] available.\n#                    If you have -proxy specified, specify -uamlist \"\" to \n#                    prevent ddp connections from working.\n#\n#     -transall      Make both available\n#\n#   Transport Options:\n#     -ipaddr <ipaddress> Specifies the IP address that the server should\n#                         advertise and listens to. The default is advertise\n#                         the first IP address of the system, but to listen\n#                         for any incoming request. The network address may\n#                         be specified either in dotted-decimal format for\n#                         IPv4 or in hexadecimal format for IPv6.\n#                         This option also allows to use one machine to\n#                         advertise the AFP-over-TCP/IP settings of another\n#                         machine via NBP when used together with the -proxy\n#                         option.\n#     -server_quantum <number> \n#                         Specifies the DSI server quantum. The minimum\n#                         value is 1MB. The max value is 0xFFFFFFFF. If you \n#                         specify a value that is out of range, you'll get \n#                         the default value (currently the minimum).\n#     -admingroup <groupname>\n#                         Specifies the group of administrators who should\n#                         all be seen as the superuser when they log in.\n#                         Default is disabled.\n#     -ddpaddr x.y        Specifies the DDP address of the server.\n#                         the  default is to auto-assign an address (0.0).\n#                         this is only useful if you're running on\n#                         a multihomed host.\n#     -port <number>      Specifies the TCP port the server should respond\n#                         to (default is 548)\n#     -fqdn <name:port>   specify a fully-qualified domain name (+optional\n#                         port). this gets discarded if the server can't\n#                         resolve it. this is not honored by appleshare\n#                         clients <= 3.8.3 (default: none)\n#     -hostname <name>    Use this instead of the result from calling\n#                         hostname for dertermening which IP address to\n#                         advertise, therfore the hostname is resolved to\n#                         an IP which is the advertised. This is NOT used for\n#                         listening and it is also overwritten by -ipaddr.\n#     -proxy              Run an AppleTalk proxy server for specified\n#                         AFP/TCP server (if address/port aren't given,\n#                         then first IP address of the system/548 will\n#                         be used).\n#                         if you don't want the proxy server to act as\n#                         a ddp server as well, set -uamlist to an empty\n#                         string.\n#     -dsireadbuf [number]\n#                         Scale factor that determines the size of the\n#                         DSI/TCP readahead buffer, default is 12. This is\n#                         multiplies with the DSI server quantum (default\n#                         ~300k) to give the size of the buffer. Increasing\n#                         this value might increase throughput in fast local\n#                         networks for volume to volume copies.  Note: This\n#                         buffer is allocated per afpd child process, so\n#                         specifying large values will eat up large amount of\n#                         memory (buffer size * number of clients).\n#     -tcprcvbuf [number]\n#                         Try to set TCP receive buffer using setsockpt().\n#                         Often OSes impose restrictions on the applications\n#                         ability to set this value.\n#     -tcpsndbuf [number]\n#                         Try to set TCP send buffer using setsockpt().\n#                         Often OSes impose restrictions on the applications\n#                         ability to set this value.\n#     -slp                Register this server with the Service Location\n#                         Protocol (if SLP support was compiled in).\n#     -nozeroconf         Don't register this server with the Multicats\n#                         DNS Protocol.\n#     -advertise_ssh      Allows Mac OS X clients (10.3.3-10.4) to\n#                         automagically establish a tunneled AFP connection\n#                         through SSH. This option is not so significant\n#                         for the recent Mac OS X. See the Netatalk Manual\n#                         in detail.\n#\n#\n#   Authentication Methods:\n#     -uampath <path>  Use this path to look for User Authentication Modules.\n#                      (default: /opt/retronas/bin/netatalk2x/etc/netatalk/uams)\n#     -uamlist <a,b,c> Comma-separated list of UAMs.\n#                      (default: uams_dhx.so,uams_dhx2.so)\n#\n#                      some commonly available UAMs:\n#                      uams_guest.so: Allow guest logins\n#\n#                      uams_clrtxt.so: (uams_pam.so or uams_passwd.so)\n#                                     Allow logins with passwords\n#                                     transmitted in the clear. \n#\n#                      uams_randnum.so: Allow Random Number and Two-Way\n#                                      Random Number exchange for\n#                                      authentication.\n#\n#                      uams_dhx.so: (uams_dhx_pam.so or uams_dhx_passwd.so)\n#                                  Allow Diffie-Hellman eXchange\n#                                  (DHX) for authentication.\n#\n#                      uams_dhx2.so: (uams_dhx2_pam.so or uams_dhx2_passwd.so)\n#                                   Allow Diffie-Hellman eXchange 2\n#                                   (DHX2) for authentication.\n#\n#   Password Options:\n#     -[no]savepassword   [Don't] Allow clients to save password locally\n#     -passwdfile <path>  Use this path to store Randnum passwords.\n#                         (Default: /opt/retronas/bin/netatalk2x/etc/netatalk/afppasswd. The only other\n#                         useful value is ~/.passwd. See 'man afppasswd'\n#                         for details.)\n#     -passwdminlen <#>   minimum password length. may be ignored.\n#     -[no]setpassword    [Don't] Allow clients to change their passwords.\n#     -loginmaxfail <#>   maximum number of failed logins. this may be\n#                         ignored if the uam can't handle it.\n#\n#   AppleVolumes files:\n#     -defaultvol <path>  Specifies path to AppleVolumes.default file\n#                         (default /opt/retronas/bin/netatalk2x/etc/netatalk/AppleVolumes.default,\n#                         same as -f on command line)\n#     -systemvol <path>   Specifies path to AppleVolumes.system file\n#                         (default /opt/retronas/bin/netatalk2x/etc/netatalk/AppleVolumes.system,\n#                         same as -s on command line)\n#     -[no]uservolfirst   [Don't] read the user's ~/AppleVolumes or\n#                         ~/.AppleVolumes before reading\n#                         /opt/retronas/bin/netatalk2x/etc/netatalk/AppleVolumes.default\n#                         (same as -u on command line)\n#     -[no]uservol        [Don't] Read the user's volume file\n#     -closevol           Immediately unmount volumes removed from\n#                         AppleVolumes files on SIGHUP sent to the afp\n#                         master process.\n#\n#   Miscellaneous:\n#     -authprintdir <path> Specifies the path to be used (per server) to \n#                          store the files required to do CAP-style\n#                          print authentication which papd will examine\n#                          to determine if a print job should be allowed.\n#                          These files are created at login and if they\n#                          are to be properly removed, this directory\n#                          probably needs to be umode 1777\n#     -guestname \"user\"   Specifies the user name for the guest login\n#                         (default \"nobody\", same as -g on command line)\n#     -loginmesg \"Message\"  Client will display \"Message\" upon logging in\n#                         (no default, same as -l \"Message\" on commandline)\n#     -nodebug            Switch off debugging\n#     -client_polling     With this switch enabled, afpd won't advertise\n#                         that it is capable of server notifications, so that\n#                         connected clients poll the server every 10 seconds\n#                         to detect changes in opened server windows.\n#                         Note: Depending on the number of simultaneously\n#                         connected clients and the network's speed, this can\n#                         lead to a significant higher load on your network!\n#     -sleep   <number>   AFP 3.x wait number hours before disconnecting\n#                         clients in sleep mode. Default 10 hours\n#     -tickleval <number> Specify the tickle timeout interval (in seconds).\n#                         Note, this defaults to 30 seconds, and really \n#                         shouldn't be changed.  If you want to control\n#                         the server idle timeout, use the -timeout option.\n#                         A value of 0 disables session timer tickles.\n#     -timeout <number>   Specify the number of tickles to send before\n#                         timing out a connection.\n#                         The default is 4, therefore a connection will\n#                         timeout in 2 minutes.\n#     -[no]icon           [Don't] Use the platform-specific icon. Recent\n#                         Mac OS don't display it any longer.\n#     -volnamelen <number>\n#                         Max length of UTF8-MAC volume name for Mac OS X.\n#                         Note that Hangul is especially sensitive to this.\n#                           255: limit of spec\n#                           80:  limit of generic Mac OS X (default)\n#                           73:  limit of Mac OS X 10.1, if >= 74\n#                                Finder crashed and restart repeatedly.\n#                         Mac OS 9 and earlier is not influenced by this,\n#                         Maccharset volume names are always limitted to 27.\n#     -[un]setuplog \"<logtype> <loglevel> [<filename>]\"\n#                         Specify that any message of a loglevel up to the\n#                         given loglevel should be logged to the given file.\n#                         If the filename is ommited the loglevel applies to\n#                         messages passed to syslog.\n#\n#                         By default (no explicit -setuplog and no buildtime\n#                         configure flag --with-logfile) afpd logs to syslog\n#                         with a default logging setup equivalent to\n#                         \"-setuplog default log_info\".\n#\n#                         If build with --with-logfile[=somefile]\n#                         (default logfile /var/log/netatalk.log) afpd\n#                         defaults to a setup that is equivalent to\n#                         \"-setuplog default log_info [netatalk.log|somefile]\"\n#\n#                         logtypes:  Default, AFPDaemon, Logger, UAMSDaemon\n#                         loglevels: LOG_SEVERE, LOG_ERROR, LOG_WARN,\n#                                    LOG_NOTE, LOG_INFO, LOG_DEBUG,\n#                                    LOG_DEBUG6, LOG_DEBUG7, LOG_DEBUG8,\n#                                    LOG_DEBUG9, LOG_MAXDEBUG\n#\n#                Example: Useful default config\n#                         -setuplog \"default log_info /var/log/afpd.log\"\n#\n#                         Debugging config\n#                         -setuplog \"default log_maxdebug /var/log/afpd.log\"\n#\n#     -signature { user:<text> | auto }\n#                         Specify a server signature. This option is useful\n#                         while running multiple independent instances of\n#                         afpd on one machine (eg. in clustered environments,\n#                         to provide fault isolation etc.).\n#                         Default is \"auto\".\n#                         \"auto\" signature type allows afpd generating\n#                         signature and saving it to afp_signature.conf\n#                         automatically (based on random number).\n#                         \"host\" signature type switches back to \"auto\"\n#                         because it is obsoleted.\n#                         \"user\" signature type allows administrator to\n#                         set up a signature string manually.\n#                         Examples: three servers running on one machine:\n#                               first   -signature user:USERS\n#                               second  -signature user:USERS\n#                               third   -signature user:ADMINS\n#                         First two servers will act as one logical AFP\n#                         service. If user logs in to first one and then\n#                         connects to second one, session will be\n#                         automatically redirected to the first one. But if\n#                         client connects to first and then to third, \n#                         will be asked for password twice and will see\n#                         resources of both servers.\n#                         Traditional method of signature generation causes\n#                         two independent afpd instances to have the same\n#                         signature and thus cause clients to be redirected\n#                         automatically to server (s)he logged in first.\n#     -k5keytab <path>\n#     -k5service <service>\n#     -k5realm <realm>\n#                         These are required if the server supports\n#                         Kerberos 5 authentication\n#     -ntdomain\n#     -ntseparator\n#                         Use for eg. winbind authentication, prepends\n#                         both strings before the username from login and\n#                         then tries to authenticate with the result\n#                         through the availabel and active UAM authentication\n#                         modules.\n#     -dircachesize entries\n#                         Maximum possible entries in the directory cache.\n#                         The cache stores directories and files. It is used\n#                         to cache the full path to directories and CNIDs\n#                         which considerably speeds up directory enumeration.\n#                         Default size is 8192, maximum size is 131072. Given\n#                         value is rounded up to nearest power of 2. Each\n#                         entry takes about 100 bytes, which is not much, but\n#                         remember that every afpd child process for every\n#                         connected user has its cache.\n#     -fcelistener host[:port]\n#                         Enables sending FCE events to the specified host,\n#                         default port is 12250 if not specified. Specifying\n#                         mutliple listeners is done by having this option\n#                         once for each of them.\n#     -fceevents fmod,fdel,ddel,fcre,dcre,tmsz\n#                         Speficies which FCE events are active, default is\n#                         fmod,fdel,ddel,fcre,dcre.\n#     -fcecoalesce all|delete|create\n#                         Coalesce FCE events.\n#     -fceholdfmod seconds\n#                         This determines the time delay in seconds which is\n#                         always waited if another file modification for the\n#                         same file is done by a client before sending an FCE\n#                         file modification event (fmod). For example saving\n#                         a file in Photoshop would generate multiple events\n#                         by itself because the application is opening,\n#                         modifying and closing a file mutliple times for\n#                         every \"save\". Defautl: 60 seconds.\n#     -keepsessions       Enable \"Continuous AFP Service\". This means the\n#                         ability to stop the master afpd process with a\n#                         SIGQUIT signal, possibly install an afpd update and\n#                         start the afpd process. Existing AFP sessions afpd\n#                         processes will remain unaffected. Technically they\n#                         will be notified of the master afpd shutdown, sleep\n#                         15-20 seconds and then try to reconnect their IPC\n#                         channel to the master afpd process. If this\n#                         reconnect fails, the sessions are in an undefined\n#                         state. Therefor it's absolutely critical to restart\n#                         the master process in time!\n#     -noacl2maccess      Don't map filesystem ACLs to effective permissions.\n#\n#   Codepage Options:\n#     -unixcodepage <CODEPAGE>  Specifies the servers unix codepage,\n#                               e.g. \"ISO-8859-15\" or \"UTF8\".\n#                               This is used to convert strings to/from\n#                               the systems locale, e.g. for authenthication.\n#                               Defaults to LOCALE if your system supports it,\n#                               otherwise ASCII will be used.\n#\n#     -maccodepage <CODEPAGE>   Specifies the legacy clients (<= Mac OS 9)\n#                               codepage, e.g. \"MAC_ROMAN\".\n#                               This is used to convert strings to the\n#                               systems locale, e.g. for authenthication\n#                               and SIGUSR2 messaging. This will also be\n#                               the default for volumes maccharset.\n#\n#   CNID related options:\n#     -cnidserver <ipaddress:port>\n#                               Specifies the IP address and port of a\n#                               cnid_metad server, required for CNID dbd\n#                               backend. Defaults to localhost:4700.\n#                               The network address may be specified either\n#                               in dotted-decimal format for IPv4 or in\n#                               hexadecimal format for IPv6.\n#\n#   Avahi (Bonjour) related options:\n#     -mimicmodel <model>\n#                               Specifies the icon model that appears on\n#                               clients. Defaults to off. Examples: RackMac\n#                               (same as Xserve), PowerBook, PowerMac, Macmini,\n#                               iMac, MacBook, MacBookPro, MacBookAir, MacPro,\n#                               AppleTV1,1, AirPort\n#\n\n\n#\n# Some examples:\n#\n#       The simplest case is to not have an afpd.conf.\n#\n#       4 servers w/ names server1-3 and one w/ the hostname. servers\n#       1-3 get routed to different ports with server 3 being bound \n#       specifically to address 192.168.1.3\n#\n#           -\n#           server1 -port 12000\n#           server2 -port 12001\n#           server3 -port 12002 -ipaddr 192.168.1.3\n#\n#       a dedicated guest server, a user server, and a special\n#       AppleTalk-only server:\n#\n#           \"Guest Server\" -uamlist uams_guest.so \\\n#                   -loginmesg \"Welcome guest! I'm a public server.\"\n#           \"User Server\" -uamlist uams_dhx2.so -port 12000\n#           \"special\" -ddp -notcp -defaultvol <path> -systemvol <path>\n#\n\n\n# default:\n# - -tcp -noddp -uamlist uams_dhx.so,uams_dhx2.so\n- -transall -hostname \"retroafp\" -uamlist uams_guest.so,uams_clrtxt.so,uams_randnum.so,uams_dhx.so,uams_dhx2.so\n"
  },
  {
    "path": "ansible/templates/install_netatalk2x/afpexpect.sh.j2",
    "content": "#!/usr/bin/expect -f\nset timeout -1\nset USERNAME [lindex $argv 0]\nset PASSWORD [lindex $argv 1]\n\nspawn /opt/retronas/bin/netatalk2x/bin/afppasswd -a $USERNAME\n\nexpect \"Enter NEW AFP password:\"\nsend \"$PASSWORD\\r\"\n\nexpect \"Enter NEW AFP password again:\"\nsend \"$PASSWORD\\r\"\n\nexpect eof\n"
  },
  {
    "path": "ansible/templates/install_netatalk2x/atalkd.conf.j2",
    "content": "#\n# Format of lines in this file:\n#\n#    interface [ -seed ] [ -router | -dontroute ] \n#       [ -phase { 1 | 2 } ] [ -addr net.node ]\n#\t[ -net first[-last] ] [ -zone ZoneName ] ...\n#\n# -seed only works if you have multi-interfaces.  Any missing arguments are\n# automatically configured from the network.  Note: lines can't actually be\n# split, tho it's a good idea.\n#\n# -router is like -seed but it allows single-interface routing. -dontroute \n# disables routing for the specified interface.\n#\n# Some examples:\n#\n#\tThe simplest case is no atalkd.conf.  This works on most platforms\n#\t(notably not Solaris), since atalkd can discover the local interfaces\n#\ton the machine.\n#\n#\tVery slightly more complicated:\n#\n#\t\tle0\n#\tor\n#\t\teth0\n#\n#\tfor Solaris/SunOS or Linux.\n#\n#\tA much more complicated example:\n#\n#\t\tle0 -phase 1\n#\t\tle1 -seed -phase 2 -addr 66.6 -net 66-67 -zone \"No Parking\"\n#\n#\tThis turns on transition routing between the le0 and le1\n#\tinterfaces on a Sun.  It also causes atalkd to fail if other\n#\trouters disagree about it's configuration of le1.\n#\n{{ ansible_default_ipv4.interface }} -router -phase 2 -net 1 -zone \"retroafp\"\n"
  },
  {
    "path": "ansible/templates/install_netatalk2x/default.j2",
    "content": "# Netatalk 2.x configuration\n\n#########################################################################\n# Global configuration\n#########################################################################\n\n#### machine's AFPserver/AppleTalk name.\nATALK_NAME=retroafp\n\n#### server (unix) and legacy client (<= Mac OS 9) charsets\nATALK_UNIX_CHARSET='LOCALE'\nATALK_MAC_CHARSET='MAC_ROMAN'\n\n#### Don't Edit. export the charsets, read form ENV by apps\nexport ATALK_UNIX_CHARSET\nexport ATALK_MAC_CHARSET\n\n#########################################################################\n# AFP specific configuration\n#########################################################################\n\n#### Set which daemons to run.\n#### If you use AFP file server, run both cnid_metad and afpd.\nCNID_METAD_RUN=yes\nAFPD_RUN=yes\n\n#### maximum number of clients that can connect:\n#AFPD_MAX_CLIENTS=20\n\n#### UAMs (User Authentication Modules)\n#### available options: uams_dhx.so, uams_dhx2.so, uams_guest.so,\n####                    uams_clrtxt.so(legacy), uams_randnum.so(legacy)\nAFPD_UAMLIST=\"-U uams_dhx2.so,uams_clrtxt.so,uams_dhx.so,uams_guest.so,uams_randnum.so\"\n\n#### Set the id of the guest user when using uams_guest.so\n#AFPD_GUEST=nobody\n\n#### config for cnid_metad. Default log config:\n#CNID_CONFIG=\"-l log_note\"\n\n#########################################################################\n# AppleTalk specific configuration (legacy)\n#########################################################################\n\n#### Set which legacy daemons to run.\n#### If you need AppleTalk, run atalkd.\n#### papd, timelord and a2boot are dependent upon atalkd.\nATALKD_RUN=yes\nPAPD_RUN=yes\nTIMELORD_RUN=yes\nA2BOOT_RUN=yes\n\n#### Control whether the daemons are started in the background.\n#### If it is dissatisfied that legacy atalkd starts slowly, set \"yes\".\n#### In case using systemd/systemctl, this is not so significant.\nATALK_BGROUND=yes\n\n#### Set the AppleTalk Zone name.\n#### NOTE: if your zone has spaces in it, you're better off specifying\n####       it in atalkd.conf\n#ATALK_ZONE=@zone\n"
  },
  {
    "path": "ansible/templates/install_netatalk2x/install_netatalk2x.sh.j2",
    "content": "#!/bin/bash\n\n## RetroNAS Netatalk-2.x install script\n\n# Set world readable/executable umask\numask 0022\n\n# Set the install dir\nIDIR=\"{{ retronas_root }}/bin/netatalk2x\"\nmkdir -p \"${IDIR}\"\n\n# make/clean the source build location\nmkdir -p \"{{ retronas_root }}/src\"\ncd \"{{ retronas_root }}/src\"\nrm -rf Netatalk-2.x\n\n# Clone the source code from SourceForge\ngit clone https://github.com/rdmark/Netatalk-2.x.git\ncd Netatalk-2.x\ngit checkout $(git tag | grep netatalk-2 | tail -n1)\n\n./bootstrap\n./configure --prefix=${IDIR} --enable-systemd --enable-ddp --enable-a2boot --enable-cups --enable-timelord --enable-zeroconf\nmake -j$(nproc)\nmake install\n\n"
  },
  {
    "path": "ansible/templates/install_netatalk3/install_netatalk3.sh.j2",
    "content": "#!/bin/bash\n\n## RetroNAS Netatalk-3 install script\nTRKRVER=$(dpkg -l | awk -F'-' '/libtracker-miner/{print $3}')\n\n# Set world readable/executable umask\numask 0022\n\n# Set the install dir\nIDIR=\"{{ retronas_root }}/bin/netatalk3\"\nmkdir -p \"${IDIR}\"\n\n# make/clean the source build location\nmkdir -p \"{{ retronas_root }}/src\"\ncd \"{{ retronas_root }}/src\"\nrm -rf netatalk\n\n# Clone the source code from SourceForge\ngit clone https://github.com/netatalk/netatalk.git\ncd netatalk\ngit checkout $(git tag | grep netatalk-3 | tail -n1)\n\n\nOPTIONS=()\nOPTIONS+=\" --prefix=${IDIR}\"\nOPTIONS+=\" --with-init-style=debian-systemd\"\nOPTIONS+=\" --without-libevent\"\nOPTIONS+=\" --without-tdb\"\nOPTIONS+=\" --with-cracklib\"\nOPTIONS+=\" --enable-krbV-uam\"\nOPTIONS+=\" --with-pam-confdir=/etc/pam.d\"\nOPTIONS+=\" --with-dbus-daemon=/usr/bin/dbus-daemon\"\nOPTIONS+=\" --with-dbus-sysconf-dir=/etc/dbus-1/system.d\"\n\n# spotlight support requires libtracker-miner\nif [ ! -z $TRKRVER ]\nthen \n        [ ! -f /usr/bin/tracker ] && ln -sf /usr/bin/tracker3 /usr/bin/tracker \n        OPTIONS+=\" --with-tracker-pkgconfig-version=$TRKRVER\"\nfi\n\n./bootstrap\n./configure $OPTIONS\nmake -j$(nproc)\nmake install\n"
  },
  {
    "path": "ansible/templates/install_netatalk3/netatalk.service.j2",
    "content": "[Unit]\nDescription=Netatalk AFP fileserver for Macintosh clients\nDocumentation=man:afp.conf(5) man:netatalk(8) man:afpd(8) man:cnid_metad(8) man:cnid_dbd(8)\nDocumentation=http://netatalk.sourceforge.net/\nAfter=network.target avahi-daemon.service\n\n[Service]\nType=forking\nGuessMainPID=no\nExecStart={{ retronas_root }}/bin/netatalk3/sbin/netatalk\nPIDFile=/var/lock/netatalk\nExecReload=/bin/kill -HUP $MAINPID\nRestart=always\nRestartSec=1\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "ansible/templates/install_netatalk3/retronas.conf.j2",
    "content": "## RetroNAS config\n## This is autogenerated.  Changes will be lost.\n[retronas]\npath = {{ retronas_path }}\nrwlist = {{ retronas_user }}\n"
  },
  {
    "path": "ansible/templates/install_netatalk4/atalkd.service.j2",
    "content": "[Unit]\nDescription=Netatalk Appletalk service\nDocumentation=man:atalkd.conf(5)\nDocumentation=https://netatalk.io/\nAfter=network.target avahi-daemon.service\nBefore=netatalk.service\n\n[Service]\nType=forking\nGuessMainPID=no\nExecStart={{ retronas_root }}/bin/netatalk4/sbin/atalkd\nPIDFile=/var/lock/atalkd\nExecReload=/bin/kill -HUP $MAINPID\nRestart=always\nRestartSec=1\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "ansible/templates/install_netatalk4/install_netatalk4.sh.j2",
    "content": "#!/bin/bash\n\n## RetroNAS Netatalk-4 install script\nTRKRVER=$(dpkg -l | awk -F'-' '/libtracker-miner/{print $3}')\n\n# Set world readable/executable umask\numask 0022\n\n# Set the install dir\nIDIR=\"{{ retronas_root }}/bin/netatalk4\"\nmkdir -p \"${IDIR}\"\n\n# make/clean the source build location\nmkdir -p \"{{ retronas_root }}/src\"\ncd \"{{ retronas_root }}/src\"\nrm -rf netatalk\n\n# Clone the source code from SourceForge\ngit clone https://github.com/netatalk/netatalk.git\ncd netatalk\ngit checkout $(git describe --tags --abbrev=0 | grep netatalk-4 | tail -n1)\n\n\nOPTIONS=\"\"\nOPTIONS+=\" -Dprefix=${IDIR}\"\nOPTIONS+=\" -Dwith-appletalk=true\"\nOPTIONS+=\" -Dwith-zeroconf=true\"\nOPTIONS+=\" -Dwith-spotlight=true\"\nOPTIONS+=\" -Dwith-init-style=systemd\"\nOPTIONS+=\" -Dwith-cracklib=true\"\nOPTIONS+=\" -Dwith-krbV-uam=true\"\nOPTIONS+=\" -Dwith-pam-config-path=/etc/pam.d\"\n# as per https://dbus.freedesktop.org/doc/dbus-daemon.1.html\nOPTIONS+=\" -Dwith-dbus-daemon-path=/usr/bin/dbus-daemon\"\nOPTIONS+=\" -Dwith-dbus-sysconf-path=/usr/share/dbus-1/system.d\"\n\n# spotlight support requires libtracker-miner\nif [ ! -z $TRKRVER ]\nthen \n        [ ! -f /usr/bin/tracker ] && ln -sf /usr/bin/tracker3 /usr/bin/tracker \n        OPTIONS+=\" --with-tracker-pkgconfig-version=$TRKRVER\"\nfi\n\nmeson setup build $OPTIONS\nmeson compile -C build\n\nif [ $? -eq 0 ]\nthen\n  sudo meson install -C build\n  exit 0\nfi\n\necho \"Build failed! exiting\"\nexit 1\n"
  },
  {
    "path": "ansible/templates/install_netatalk4/netatalk.service.j2",
    "content": "[Unit]\nDescription=Netatalk AFP fileserver\nDocumentation=man:afp.conf(5) man:netatalk(8) man:afpd(8) man:cnid_metad(8) man:cnid_dbd(8)\nDocumentation=https://netatalk.io/\nAfter=network.target avahi-daemon.service atalkd.service\n\n[Service]\nType=forking\nGuessMainPID=no\nExecStart={{ retronas_root }}/bin/netatalk4/sbin/netatalk\nPIDFile=/var/lock/netatalk\nExecReload=/bin/kill -HUP $MAINPID\nRestart=always\nRestartSec=1\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "ansible/templates/install_netatalk4/retronas.conf.j2",
    "content": "## RetroNAS config\n## This is autogenerated.  Changes will be lost.\n[retronas]\npath = {{ retronas_path }}\nrwlist = {{ retronas_user }}\n"
  },
  {
    "path": "ansible/templates/install_netlink/install_netlink.sh.j2",
    "content": "#!/bin/bash\n# still based on https://github.com/Kazade/dreampi/issues/17\n\nDPITMP=$(mktemp -d)\nOUTDIR=/opt/netlink\n\n# clone the repo and switch to the python3 branch\ncd $DPITMP\nSOURCECODE=$( curl -kLs https://api.github.com/repos/eaudunord/Netlink/releases | jq -r \".[0].assets | map(select(.name | match (\\\"Netlink_Tunnel\\\")))[-1] | .browser_download_url\" )\n[ -z \"$SOURCECODE\" ] && echo \"Couldn't get source code name\" && exit 1\necho $SOURCECODE\n\nRELEASE=$(basename \"${SOURCECODE}\") \n[ ! -f $RELEASE ] && curl -OJL \"${SOURCECODE}\"\n\n# unpack and clean\n[ ! -d $OUTDIR ] && mkdir -p $OUTDIR\nunzip $RELEASE -x \"*/winPython/*\" \"*.bat\"\ncp -R Netlink_Tunnel*/* $OUTDIR/\n\n# xband.py is missind the from the 4.4 release\nif [ ! -f $OUTDIR/xband.py ]\nthen \n    curl -o$OUTDIR/xband.py https://raw.githubusercontent.com/eaudunord/Netlink/refs/heads/main/tunnel/xband.py\nfi\n\n# pull python3 requirements from dreampi\ncurl -O https://raw.githubusercontent.com/sairuk/dreampi/master/requirements.txt\npython3 -m pip install -r requirements.txt"
  },
  {
    "path": "ansible/templates/install_netlink/netlink.conf.j2",
    "content": "domain-needed\nbogus-priv\nserver=46.101.91.123\nno-resolv\nno-hosts\ncache-size=500\nlog-queries\n"
  },
  {
    "path": "ansible/templates/install_netlink/netlink.patch.j2",
    "content": "--- tunnel.py\t2022-12-03 21:45:41.763778651 +0000\n+++ tunnel.py\t2022-09-26 14:29:52.000000000 +0100\n@@ -67,7 +67,7 @@\n def com_scanner():\n     global com_port\n     speed = 115200\n-    for i in range(1,25): # this should be a big enough range. USB com ports usually end up in the teens.\n+    for i in range(0,25): # this should be a big enough range. USB com ports usually end up in the teens.\n         osName = os.name\n         if osName == 'posix': # should work on linux and Mac for USB modem, but untested.\n             com_port = \"/dev/ttyACM%s\" % i\n"
  },
  {
    "path": "ansible/templates/install_netlink/netlink.service.j2",
    "content": "[Unit]\nDescription=netlink for Sega Saturn\nAfter=network.target\n\n[Service]\nWorkingDirectory=/opt/netlink\nExecStart=/usr/bin/python3 /opt/netlink/tunnel.py noUpdate\nRestart=on-failure\nRestartSec=30s\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "ansible/templates/install_netmount/install_netmount.sh.j2",
    "content": "#!/bin/bash\n\nset -u\n\nDEST=/opt/netmount\nTDIR=$(mktemp -d)\nOWNER=jrohel\nREPO=NetMount\n\necho \"Downloading release ...\"\n\nARCH=$(uname -m)\n\nRELEASE=$( curl -kLs https://api.github.com/repos/${OWNER}/${REPO}/releases | jq -r \".[0].assets | map(select(.name | match (\\\"netmount.*linux-${ARCH}\\\")))[-1] | .browser_download_url\" )\n[ -z \"$RELEASE\" ] && echo \"Couldn't get release for arch: ${ARCH}\" && exit 1\n\n\ncd ${TDIR}\nREL_NAME=\"$(basename ${RELEASE})\"\n\n\nif `curl -OJL \"${RELEASE}\"`\nthen\n  [ ! -d ${DEST} ] && mkdir -p ${DEST}\n  bzip2 -d ${REL_NAME}\n\n  rm netmount*.bz2\n  mv netmount* ${DEST}/\n\n  chmod +x ${DEST}/netmount*\n  find ${DEST} -executable -type f -iname \"netmount*\" -exec ln -s {} ${DEST}/netmount \\;\n\n  rm -rf ${TDIR}\n  exit 0\nfi\n\necho \"Install failed!\"\nexit 1\n"
  },
  {
    "path": "ansible/templates/install_netmount/netmount-confman.py.j2",
    "content": "#!/usr/bin/env python3\n\nimport os\nimport yaml\nimport argparse\n\n#\n# this is written specifically for RetroNAS use it is fairly basic and hacky \n# however some basic flexibilty is provided\n# - sai\n\n\nfailed = False\noutput_types = [\"systemd\"]\nbinpath = \"/opt/netmount/netmount\"\n\ndef log(s):\n    print(s)\n\ndef error(s):\n    log(f\"ERROR: {s}\")\n    failed = True\n\n\ndef check_state():\n    if failed:\n        exit(1)\n\ndef gen_systemd(cli, service_name=\"netmount\"):\n    systemd_path = \"/etc/systemd/system\"\n\n    if not os.access(systemd_path, os.W_OK):\n        error(f\"Path not writable: {systemd_path}\")\n\n    check_state()\n    service_file = os.path.join(systemd_path,f\"{service_name.lower()}.service\")\n    log(f\"Writing {service_file}\")\n\n    ncli = \" \".join([binpath, cli])\n\n    with open(service_file, \"w\") as f:\n        f.write(f\"\"\"[Unit]\nDescription={service_name}\nAfter=multi-user.target\n\n[Service]\nUser=retronas\nType=simple\nRestart=always\nExecStart={ncli}\nTimeoutStartSec=0\nRemainAfterExit=yes\n\n[Install]\nWantedBy=multi-user.target  \n\"\"\")\n\ndef format_cli(d):\n    cli = []\n\n    # program options\n    prog_options = []\n    if \"options\" in d:\n        for opt in d[\"options\"]:\n            prog_options.append(f\"--{opt}={d['options'][opt]}\")\n    cli.append(\" \".join(prog_options))\n\n    drives = []\n    for drive in d[\"drives\"]:\n        if \"letter\" not in drive:\n            error(\"letter: must be defined in config\")\n        if \"path\" not in drive:\n            error(\"path: must be defined in config\")\n\n        check_state()\n        # drive options\n        letter = drive[\"letter\"]\n        drive.pop(\"letter\")\n        path = drive[\"path\"]\n        drive.pop(\"path\")\n\n        drive_options = []\n        for do in drive:\n            drive_options.append(f\"{do}={drive[do]}\")\n        drive_options = \",\".join(drive_options)\n        \n    \n        drives.append(\",\".join([f\"{letter}={path}\",drive_options]))\n\n    cli = cli + drives\n    return(\" \".join(cli))\n\ndef main(args):\n    conf = args.conf_dir\n    output = args.output_type\n\n    for root, dirs, files in os.walk(conf):\n        for filename in files:\n            abs = os.path.join(root, filename)\n            y = {}\n            try:\n                with open(abs,'r') as f:\n                    y = yaml.safe_load(f)\n            except:\n                error(f\"Failed to read yaml in {conf}\")\n\n            check_state()\n            if not \"drives\" in y:\n                error(\"Invalid drive configuration file, drives: not found\")\n\n            check_state()\n            cli = format_cli(y)\n\n            check_state()\n            if output == \"systemd\":\n                gen_systemd(cli, y[\"name\"])\n            else:\n                error(f\"Unsupported mode {output}\")\n\n            check_state()\n\nif __name__ == \"__main__\":\n\n    parser = argparse.ArgumentParser()\n    parser.add_argument('--conf-dir', type=str, help='directory holding yaml configs for drives', default=\"{{ retronas_path }}/config/netmount\")\n    parser.add_argument('--output-type', type=str, help='output type', choices=output_types, default=\"systemd\")\n\n    args = parser.parse_args()\n    main(args)\n"
  },
  {
    "path": "ansible/templates/install_netmount/netmount-confman.sh.j2",
    "content": "#!/bin/bash\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\nCLEAR\n\npython3 /opt/retronas/scripts/netmount-confman.py\n\nsystemctl daemon-reload\n\nPAUSE\n"
  },
  {
    "path": "ansible/templates/install_netmount/retronas.yaml.j2",
    "content": "name: netmount-drives-retronas\ndrives:\n  - letter: R\n    path: {{ retronas_path }}/dos\n    label: RETRONAS\noptions:\n  translit-map-path: /opt/netmount/netmount-u2a.map\n"
  },
  {
    "path": "ansible/templates/install_network-presets-standalone/dhcpcd.conf.j2",
    "content": "# A sample configuration for dhcpcd.\n# See dhcpcd.conf(5) for details.\n\n# Allow users of this group to interact with dhcpcd via the control socket.\n#controlgroup wheel\n\n# Inform the DHCP server of our hostname for DDNS.\nhostname\n\n# Use the hardware address of the interface for the Client ID.\nclientid\n# or\n# Use the same DUID + IAID as set in DHCPv6 for DHCPv4 ClientID as per RFC4361.\n# Some non-RFC compliant DHCP servers do not reply with this set.\n# In this case, comment out duid and enable clientid above.\n#duid\n\n# Persist interface configuration when dhcpcd exits.\npersistent\n\n# Rapid commit support.\n# Safe to enable by default because it requires the equivalent option set\n# on the server to actually work.\noption rapid_commit\n\n# A list of options to request from the DHCP server.\noption domain_name_servers, domain_name, domain_search, host_name\noption classless_static_routes\n# Respect the network MTU. This is applied to DHCP routes.\noption interface_mtu\n\n# Most distributions have NTP support.\n#option ntp_servers\n\n# A ServerID is required by RFC2131.\nrequire dhcp_server_identifier\n\n# Generate SLAAC address using the Hardware Address of the interface\n#slaac hwaddr\n# OR generate Stable Private IPv6 Addresses based from the DUID\nslaac private\n\n# Example static IP configuration:\n#interface eth0\n#static ip_address=192.168.0.10/24\n#static ip6_address=fd51:42f8:caae:d92e::ff/64\n#static routers=192.168.0.1\n#static domain_name_servers=192.168.0.1 8.8.8.8 fd51:42f8:caae:d92e::1\n\n# It is possible to fall back to a static IP if DHCP fails:\n# define static profile\n#profile static_eth0\n#static ip_address=192.168.1.23/24\n#static routers=192.168.1.1\n#static domain_name_servers=192.168.1.1\n\n# fallback to static profile on eth0\n#interface eth0\n#fallback static_eth0\n#\n\n# retro lan\ninterface {{ retronas_net_retro_interface }}\nstatic ip_address={{ retronas_net_retro_ip }}/{{ retronas_net_retro_subnet }}\n#static routers={{ retronas_net_retro_router }}\nstatic domain_name_servers={{ retronas_net_retro_dns }}\n\n# retro wifi\ninterface {{ retronas_net_wifi_interface }}\nstatic ip_address={{ retronas_net_wifi_ip }}/{{ retronas_net_wifi_subnet }}\nstatic routers={{ retronas_net_wifi_router }}\nstatic domain_name_servers={{ retronas_net_wifi_dns }}\n"
  },
  {
    "path": "ansible/templates/install_network-presets-zoned/dhcpcd.conf.j2",
    "content": "# A sample configuration for dhcpcd.\n# See dhcpcd.conf(5) for details.\n\n# Allow users of this group to interact with dhcpcd via the control socket.\n#controlgroup wheel\n\n# Inform the DHCP server of our hostname for DDNS.\nhostname\n\n# Use the hardware address of the interface for the Client ID.\nclientid\n# or\n# Use the same DUID + IAID as set in DHCPv6 for DHCPv4 ClientID as per RFC4361.\n# Some non-RFC compliant DHCP servers do not reply with this set.\n# In this case, comment out duid and enable clientid above.\n#duid\n\n# Persist interface configuration when dhcpcd exits.\npersistent\n\n# Rapid commit support.\n# Safe to enable by default because it requires the equivalent option set\n# on the server to actually work.\noption rapid_commit\n\n# A list of options to request from the DHCP server.\noption domain_name_servers, domain_name, domain_search, host_name\noption classless_static_routes\n# Respect the network MTU. This is applied to DHCP routes.\noption interface_mtu\n\n# Most distributions have NTP support.\n#option ntp_servers\n\n# A ServerID is required by RFC2131.\nrequire dhcp_server_identifier\n\n# Generate SLAAC address using the Hardware Address of the interface\n#slaac hwaddr\n# OR generate Stable Private IPv6 Addresses based from the DUID\nslaac private\n\n# Example static IP configuration:\n#interface eth0\n#static ip_address=192.168.0.10/24\n#static ip6_address=fd51:42f8:caae:d92e::ff/64\n#static routers=192.168.0.1\n#static domain_name_servers=192.168.0.1 8.8.8.8 fd51:42f8:caae:d92e::1\n\n# It is possible to fall back to a static IP if DHCP fails:\n# define static profile\n#profile static_eth0\n#static ip_address=192.168.1.23/24\n#static routers=192.168.1.1\n#static domain_name_servers=192.168.1.1\n\n# fallback to static profile on eth0\n#interface eth0\n#fallback static_eth0\n#\n\n# retro lan\ninterface {{ retronas_net_retro_interface }}\nstatic ip_address={{ retronas_net_retro_ip }}/{{ retronas_net_retro_subnet }}\n#static routers={{ retronas_net_retro_router }}\nstatic domain_name_servers={{ retronas_net_retro_dns }}\n"
  },
  {
    "path": "ansible/templates/install_nfs/exports.j2",
    "content": "## Automatically created by RetroNAS\n## Changes will be lost\n\n{{ retronas_path }}  *(rw,async,fsid=99,all_squash,anonuid={{ retronas_user_id }},anongid={{ retronas_group_id }})\n"
  },
  {
    "path": "ansible/templates/install_nfs/nfs-kernel-server.j2",
    "content": "## Automatically created by RetroNAS\n## Changes will be lost\n# Number of servers to start up\nRPCNFSDCOUNT=\"8 -V 2\"\n\n# Runtime priority of server (see nice(1))\nRPCNFSDPRIORITY=0\n\n# Options for rpc.mountd.\n# If you have a port-based firewall, you might want to set up\n# a fixed port here using the --port option. For more information, \n# see rpc.mountd(8) or http://wiki.debian.org/SecuringNFS\n# To disable NFSv4 on the server, specify '--no-nfs-version 4' here\nRPCMOUNTDOPTS=\"--manage-gids\"\n\n# Do you want to start the svcgssd daemon? It is only required for Kerberos\n# exports. Valid alternatives are \"yes\" and \"no\"; the default is \"no\".\nNEED_SVCGSSD=\"\"\n\n# Options for rpc.svcgssd.\nRPCSVCGSSDOPTS=\"\"\n"
  },
  {
    "path": "ansible/templates/install_open-iscsi/iscsi-manager-target-login.sh.j2",
    "content": "#!/bin/bash\n\nSERVERIP=${1:-}\nUSERNAME=\"\"\nPASSWORD=\"\"\nIQN=\"\"\nTARGET=\"\"\nISCSICFG=/etc/iscsi/nodes\n\nlist_targets() {\n    IFS=$'\\n'\n    TARGETS=($(iscsiadm -m discovery -t st -p $1 2>/dev/null | awk -F ' ' /$SERVERIP/'{print $2\" (\"$1\")\"}') quit)\n\n    [ {{ ''.join (('$','{', '#TARGETS[@]}')) }} -le 1 ] && echo \"No targets found, exiting ... \" && exit 1\n    select TARGET in \"${TARGETS[@]}\"\n    do\n        IQN=\"$(echo ${TARGET} | awk -F ' ' '{print $1}')\"\n        [ $IQN == \"quit\" ] && echo \"User requested exit ...\" && exit 0\n        [ ! -z \"${IQN}\" ] && break\n    done\n}\n\ncheck_target() {\n    iscsiadm -m session | grep -E \"${SERVERIP}.*${IQN}\" &>/dev/null\n    if [ $? -eq 0 ]\n    then\n        echo \"Target is already connected, nothing to do, exiting ...\"\n        exit 0\n    fi\n}\n\nlogin_target() {\n    check_target\n    iscsiadm -m node -T $IQN -p $SERVERIP --login  &>/dev/null\n    DEFAULTPATH=\"${ISCSICFG}/${IQN}/${SERVERIP}*/default\"\n    if [ $? -eq 0 ]\n    then\n        if [ -f ${DEFAULTPATH} ]\n        then\n            sed -i -r 's/node.conn\\[0\\].startup.*/node.conn[0].startup = automatic/' ${DEFAULTPATH}\n        else\n            echo \"default node profile for ${TARGET} not found, cannot update node startup settings\"\n        fi\n    else\n        echo \"login to target failed, not enabling on startup\"\n    fi\n}\n\nget_ip() {\n    read -r -p \"Enter ISCSI target host ip address [ip:port (default:3620)]: \" SERVERIP\n}\n\nget_up() {\n    read -r -p \"does your ISCSI target required credentials?\" ANSWER\n\n    case ANSWER in\n        Yy)\n            read -r -p \"Enter ISCSI target username: \" USERNAME\n            read -r --password -p \"Enter ISCSI target password: \" PASSWORD\n            ;;\n        *)\n            ;;\n    esac\n}\n\n\n[ -z \"$SERVERIP\" ] && get_ip\n#[ -z \"$USERNAME\"] || [ -z \"$PASSWORD\" ] && get_up\n\nlist_targets $SERVERIP\nlogin_target $IQN\n"
  },
  {
    "path": "ansible/templates/install_pfsshell/install_pfsshell.sh.j2",
    "content": "#!/bin/bash\n\nset -u\n\nAPP=pfsshell\nREPO=https://github.com/ps2homebrew/${APP}.git\nOUTPATH=/tmp\n\nfunction _log {\n\techo \"$1\"\n}\n\n### REQUIREMENTS\nREQFAIL=0\n#[ ! -x /usr/bin/gmake ] && _log \"GMAKE not found\" && REQFAIL=1\n[ $REQFAIL -ne 0 ] && _log \"Requirements failed, see previous errors\" && exit $REQFAIL\n\n\ncd $OUTPATH\nif [ ! -f ${OUTPATH}/${APP}/.git/config ] \nthen\n\tgit clone $REPO\nelse\n\tcd ${OUTPATH}/${APP}\n\tgit pull\nfi\n\ncd ${OUTPATH}/${APP}\n\ngit submodule update --remote\nmeson setup -Denable_pfsfuse=true build\nninja -C build\nninja -C build install\n\n# CLEAN UP\nrm -Rf ${OUTPATH}/${APP}\n"
  },
  {
    "path": "ansible/templates/install_pi1541/pi1541.sh.j2",
    "content": "#!/bin/bash\n#\n# PI1541 installer\n#\n\nset -u\n\nclear\n\nTMPFILES={{ retronas_path }}/pi1541\nTARGET=/mnt/pi1541\n\nMOUNT_DRIVE() {\n\techo \"Mounting drive /dev/$1 at $TARGET\"\n\tMNTPOINT=$(mount | grep $1 | awk '{print $3}')\n\tif [ ! -z \"${MNTPOINT}\" ]\n\tthen\n\t\techo \"Drive already mounted at $MNTPOINT, wont mount\"\n\telse\n\t\techo \"Drive not mounted, will mount to $TARGET\"\n\t\tmkdir -p $TARGET\n\t\tmount -t vfat /dev/$1 $TARGET\n\t\t[ $? -eq 0 ] && echo \"  Done\"\n\tfi\n}\n\nINSTALL_PI1541() {\n\tlocal ARCHIVE=Pi1541.zip\n\tcd $TMPFILES\n\n\techo \"Pi1541 software\"\n\tif [ ! -f $TMPFILES/$ARCHIVE ]\n\tthen\n\t\techo \" $ARCHIVE not found downloading to $TMPFILES\"\n\t\tcurl -sO https://cbm-pi1541.firebaseapp.com/$ARCHIVE\n\tfi\n\t\n\techo \" Installing $ARCHIVE to $TARGET\"\n\tunzip -qq -o $ARCHIVE\n\tcp -R Pi1541/* $TARGET/\n\t[ $? -eq 0 ] && echo \"  Done\"\n\trm -rf Pi1541/\n}\n\nINSTALL_FIRMWARE() {\n\tlocal ARCHIVE=1.20180919.zip\n\tcd $TMPFILES\n\n\techo \"RPI firmware\"\n\tif [ ! -f $TMPFILES/$ARCHIVE ]\n\tthen\n\t\techo \" $ARCHIVE not found downloading to $TMPFILES\"\n\t\tcurl -sLO https://github.com/raspberrypi/firmware/archive/$ARCHIVE\n\t\t[ $? -eq 0 ] && echo \"  Done\"\n\tfi\n\n\techo \" Installing required RPI firmware to $TARGET\"\n\tunzip -qq -o -j $ARCHIVE firmware-*/boot/{bootcode.bin,fixup.dat,start.elf} -d $TARGET/\n\t[ $? -eq 0 ] && echo \"  Done\"\n}\n\nINSTALL_ROM() {\n\tlocal ARCHIVE=vice-tmp.zip\n\tcd $TMPFILES\n\n\techo \"Drive firmware and font files\"\n\tif [ ! -f $TMPFILES/$ARCHIVE ]\n\tthen\n\t\techo \" $ARCHIVE not found downloading to $TMPFILES\"\n\t\tcurl -sL -o $ARCHIVE https://sourceforge.net/projects/vice-emu/files/latest/download\n\t\t[ $? -eq 0 ] && echo \"  Done\"\n\tfi\n\n\techo \" Installing drive rom\"\n\tunzip -qq -o -j $ARCHIVE \"*/DRIVES/dos1541\" -d $TARGET/\n\t[ $? -eq 0 ] && echo \"  Done\"\n\n\techo \" Installing CBM font\"\n\tunzip -qq -o -j $ARCHIVE \"*/C64/chargen\" -d $TARGET/\n\t[ $? -eq 0 ] && echo \"  Done\"\n}\n\nCONFIG_OPTIONB() {\n\t    read -p \"Configure cable Option B support? [y/N]: \" ANSWER\n\n\t    case $ANSWER in\n\t      y|Y)\n\t\t  \tsed -i -r 's/\\/\\/(splitIECLines)/\\1/' $TARGET/options.txt\n\t\t\t[ $? -eq 0 ] && echo \"  Done\"\n            ;;\n\t      *)  \n            INSTALL_ROMS\n            ;;\n        esac\n}\n\nINSTALL_ROMS() {\n        SYSTEMS=(\n            commodore64 \n            commodore128 \n            commodore16 \n            commodoreplus4 \n            vic20 \n            exit\n        )\n\n    echo \"Select a system to copy roms for to the $TARGET\"\n\n    select SYSTEM in \"${SYSTEMS[@]}\"\n    do  \n        [ $SYSTEM == \"exit\" ] && echo \"Exiting...\" && CLEANUP\n        ROMPATH=\"{{ retronas_path }}/roms/commodore/$SYSTEM\"\n\n        echo \"$(df -h | grep \"$TARGET\" | awk '{print $1, \"has\", $4, \"free\"}') $(du -hs $ROMPATH | awk '{print $1, \"is required\"}')\"\n\t    read -p \"Continue? [y/N]: \" ANSWER\n\n\t    case $ANSWER in\n\t      y|Y)\n            if [ ! -z \"$SYSTEM\" ]\n            then\n                ROMSYS=\"$TARGET/1541/$SYSTEM\"\n                echo \"Copying roms from $ROMPATH\"\n                [ ! -d \"$ROMSYS\" ] && mkdir -p \"$ROMSYS\"\n                cp -R \"$ROMPATH/\"* \"$ROMSYS\"\n\t\t\t\t[ $? -eq 0 ] && echo \"  Done\"\n                INSTALL_ROMS\n\n            fi\n            ;;\n\t      *)  \n            INSTALL_ROMS\n            ;;\n        esac\n    done\n}\n\nCLEANUP() {\n\techo \"Unmounting $TARGET\"\n\tumount $TARGET\n\trmdir $TARGET\n\n\texit 0\n}\n\nSELECT_DRIVE() {\n\tFAT32DEVS=($(lsblk -f -r | grep -v boot | awk '/FAT32/{print $1}' ) exit)\n\n\n\t{# this workaround is for broken bash array count support in jinja #}\n\tif [ {{ ['${','#FAT32DEVS[@]}']|join('') }} -le 1 ]\n\tthen\n\t\techo \"No available FAT32 drives found, insert one and run this script again\"\n\t\tsleep 3\n\t\texit 1\n\tfi\n\n\techo \"Available FAT32 devices:\"\n\tselect DRIVE in \"${FAT32DEVS[@]}\"\n\tdo\n\t    [ $DRIVE == \"exit\" ] && echo \"Exiting...\" && exit 0\n\t    \n\t    read -p \"($DRIVE) Are you sure? [y/N]: \" ANSWER\n\n\t    case $ANSWER in\n\t      y|Y)\n\t\tif [ ! -z \"$DRIVE\" ]\n\t\tthen\n\t\t\tMOUNT_DRIVE $DRIVE\n\t\t\tINSTALL_FIRMWARE\n\t\t\tINSTALL_PI1541\n\t\t\tINSTALL_ROM\n\t\t\tCONFIG_OPTIONB\n\t\t\tINSTALL_ROMS\n\t\t\tCLEANUP\n\t\tfi\n\t\texit\n\t\t;;\n\t      *)  \n\t\tSELECT_DRIVE $1\n\t\t;;\n\t    esac\n\tdone\n}\n\n\ncat << TITLE\n*********************************************\n*\n* Pi1541 installer\n*\n* You will be prompted for a drive to setup\n* for use with Pi1541\n*\n*********************************************\nTITLE\n\nSELECT_DRIVE\n"
  },
  {
    "path": "ansible/templates/install_piscsi/install_piscsi.sh.j2",
    "content": "#!/bin/bash\n\nset -u\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\n\nAPP=piscsi\nREPO=https://github.com/PiSCSI/${APP}.git\nSRCDIR={{ retronas_root }}/src/\nRPI=0\nCONNECT_TYPE=${1:-fullspec}\nOUTDIR={{ retronas_root }}/bin/${APP}\n\nfunction _log {\n        echo \"$1\"\n}\n\n### REQUIREMENTS\nREQFAIL=0\n[ $REQFAIL -ne 0 ] && _log \"Requirements failed, see previous errors\" && exit $REQFAIL\n\nif `grep -Eqq \"^Raspberry.*\" /proc/device-tree/model`\nthen\n\t# X64 remote access build flags\n\t# EXTRA_FLAGS=\"-DSPDLOG_FMT_EXTERNAL -DFMT_HEADER_ONLY\"\n\t_log \"Unsupported OS, only for RPI\"\n\tsleep 3\n\texit 1\nfi\n\n[ ! -d $SRCDIR ] && mkdir -p $SRCDIR\ncd $SRCDIR\n\nif [ ! -f ${SRCDIR}/${APP}/.git/config ]\nthen\n\tgit clone $REPO\nelse\n\tcd ${SRCDIR}/${APP}\n\tgit clean -d -f\n    git reset --hard\n\tgit checkout main\n\tgit pull\nfi\n\nLATEST=$(git tag | tail -n1)\ngit checkout $LATEST\n\n# BUILD TYPE\ncase $CONNECT_TYPE in\n\tfullspec)\n\t\t\t# FULL SPEC BOARD\n\t\t\tCONNECT_TYPE_BUILD=FULLSPEC\n\t\t\t;;\n\tstandard)\n\t\t\t# STANDARD BOARD\n\t\t\tCONNECT_TYPE_BUILD=STANDARD\n\t\t\t;;\n\t*)\n\t\t\t_log \"Please pass a board type fullspec or standard\"\n\t\t\texit 1\nesac\n\n# PATCH MAKEFILE\n#git apply -3 {{ retronas_root }}/src/piscsi_retronas_patch.diff\n\n# BUILD\n#BUILD_DIR=${SRCDIR}/${APP}/src/raspberrypi\nBUILD_DIR=${SRCDIR}/${APP}/cpp/\n\ncd ${BUILD_DIR}\nmake clean\nmake all CONNECT_TYPE=${CONNECT_TYPE_BUILD} -j$(nproc)\n\n# Copy BINs\nif [ -f ${BUILD_DIR}/bin/piscsi ]\nthen\n\n\t[ ! -d \"${OUTDIR}\" ] && mkdir -p \"${OUTDIR}\"\n\n\tSERVICE=piscsi.service\n\tSTARTED=$(systemctl show $SERVICE --full --property SubState --value)\n\t[ $STARTED == \"running\" ] && systemctl stop $SERVICE\n\n\tcp -f ${BUILD_DIR}/bin/* \"${OUTDIR}/\"\n\tsystemctl restart $SERVICE\n\nfi"
  },
  {
    "path": "ansible/templates/install_piscsi/install_piscsi_standard.sh.j2",
    "content": "#!/bin/bash\n\n./install_piscsi.sh standard"
  },
  {
    "path": "ansible/templates/install_piscsi/piscsi.service.j2",
    "content": "[Unit]\nDescription=piscsi\nAfter=multi-user.target\n\n[Service]\nType=simple\nRestart=always\nWorkingDirectory={{ retronas_root }}/bin/piscsi\nEnvironment=PATH=$PATH:{{ retronas_root }}/bin/piscsi\nExecStart={{ retronas_root }}/bin/piscsi/piscsi -F /home/{{ retronas_user }}/piscsi -P /etc/piscsi_passwd\nTimeoutStartSec=0\nRemainAfterExit=yes\n\n[Install]\nWantedBy=multi-user.target"
  },
  {
    "path": "ansible/templates/install_piscsi/piscsi_retronas_patch.diff.j2",
    "content": "diff --git a/src/raspberrypi/Makefile b/src/raspberrypi/Makefile\nindex 5a09b4a..33c16cb 100644\n--- a/src/raspberrypi/Makefile\n+++ b/src/raspberrypi/Makefile\n@@ -9,8 +9,8 @@ GCCVERSION10 := $(shell expr `gcc -dumpversion` \\>= 10)\n ##                   arm-linux-gnueabihf-\n CROSS_COMPILE =\n \n-CC = $(CROSS_COMPILE)gcc\n-CXX = $(CROSS_COMPILE)g++\n+CC = $(CROSS_COMPILE)gcc-9\n+CXX = $(CROSS_COMPILE)g++-9\n \n ##   DEBUG=1  : A Debug build includes the debugger symbols\n ##              and disables compiler optimization. Typically,\n@@ -22,7 +22,7 @@ ifeq ($(DEBUG), 1)\n \tBUILD_TYPE = Debug\n else\n \t# Release compiler flags\n-\tCXXFLAGS += -O3 -Wall -Werror -Wextra -DNDEBUG\n+\tCXXFLAGS += -O3 -Wall -Wextra -DNDEBUG\n \tBUILD_TYPE = Release\n endif\n ifeq (\"$(shell uname -s)\",\"Linux\")\n "
  },
  {
    "path": "ansible/templates/install_proftpd/ftp.service.j2",
    "content": "<?xml version=\"1.0\" standalone='no'?>\n<!DOCTYPE service-group SYSTEM \"avahi-service.dtd\">\n<service-group>\n  <name>retroftp</name>\n  <service>\n    <type>_ftp._tcp</type>\n    <port>21</port>\n  </service>\n</service-group>\n"
  },
  {
    "path": "ansible/templates/install_proftpd/retronas.conf.j2",
    "content": "## RetroNAS Configuration\n## This is autogenerated. Changes will be lost.\n\n# Listen on all IPs\nDefaultAddress 0.0.0.0\n\n# Set the server name\nServerName \"RetroNAS\"\n\n# Set the path for authorised users\nDefaultRoot \"{{ retronas_path }}\"\n\n# Set the Linux on-disk permissions to match the RetroNAS user\nUser \"{{ retronas_user }}\"\nGroup \"{{ retronas_group }}\"\n\n# Allow writes from the RetroNAS user\n<Directory \"{{ retronas_path }}\">\n  <Limit ALL>\n    AllowUser \"{{ retronas_user }}\"\n  </Limit>\n</Directory>\n\n# Set the path and read-only for anonymous user\n<Anonymous \"{{ retronas_path }}\"> \n  # Don't require password for anonymous\n  AnonRequirePassword off\n  # Map read-only permissions to RetroNAS user\n  UserAlias anonymous \"{{ retronas_user }}\"\n  # Deny writes for anonymous user\n  <Directory \"{{ retronas_path }}\">\n    <Limit WRITE>\n      DenyAll\n    </Limit>\n  </Directory>\n</Anonymous>\n"
  },
  {
    "path": "ansible/templates/install_ps2_openps2loader/retronas_ps2.conf.j2",
    "content": "## RetroNAS config.\n## This is autogenerated. Changes will be lost.\ncomment = ps2\npath = {{ retronas_path }}/ps2/OpenPS2Loader\nguest ok = no\nbrowseable = yes\nwrite list = {{ retronas_user }}\nwriteable = yes\nvalid users = {{ retronas_user }}\ncreate mask = 0775\ndirectory mask = 0775\nfollow symlinks = yes\nwide links = yes\nstrict sync = no\nsync always = yes\n"
  },
  {
    "path": "ansible/templates/install_ps2_udpbd/install_ps2_udpbd.sh.j2",
    "content": "#!/bin/bash\n\nSRCDIR=\"{{ retronas_root }}/src\"\nBINDIR=\"{{ retronas_root }}/bin\"\nREPO=\"https://gitlab.com/ps2max/udpbd-server.git\"\nBASE=$( basename $REPO .git )\nARCH=$(uname -m)\n\necho \"Configuring build directories...\"\nrm -rf \"${SRCDIR}\"\nmkdir -p \"${SRCDIR}\"\ncd \"${SRCDIR}\"\n\necho \"Downloading {{ my_name }} source code... from $REPO\"\n\ngit clone $REPO\ncd $BASE\nmake build\n\nif [ -f build/udpbd-server.${ARCH} ]\nthen\n    echo \"Moving binary to RetroNAS bin dir...\"\n    mkdir -p \"${BINDIR}\" 2>/dev/null\n    mv -vf build/udpbd-server.${ARCH} \"${BINDIR}\"/udpbd-server\n\n    echo \"Cleaning up...\"\n    rm -rf \"${SRCDIR}\" \n\n    echo \"All done!\"\nelse\n    echo \"Build failed\"\nfi\n"
  },
  {
    "path": "ansible/templates/install_ps2_udpbd/ps2_udpbd.service.j2",
    "content": "[Unit]\nDescription=UDPBD for PlayStation2\nAfter=network.target\nStartLimitIntervalSec=60\nStartLimitBurst=4\n\n[Service]\nUser={{ retronas_user }}\nEnvironment=\"UDPBD_DEVICE={{ retronas_path }}/{{ my_name }}/retronas_udpbd.img\"\nExecStart={{ retronas_root }}/bin/udpbd-server ${UDPBD_DEVICE}\nRestart=on-failure\nRestartSec=1\nSuccessExitStatus=3 4\nRestartForceExitStatus=3 4\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "ansible/templates/install_ps2_udpbd/udpbd_manager.sh.j2",
    "content": "#!/bin/bash\n\nselect_device() {\n    IFS=$'\\n'\n    DEVICE=()\n    DEVICES+=($(lsblk -sl 2>/dev/null | awk '/(disk|part|rom)/{print $1\" (\"$4\")\"}' | sort -u))\n    DEVICES+=($(ls -la {{ retronas_path }}/images/* 2>/dev/null | awk '{print $9}'))\n    DEVICES+=(\"quit\")\n\n    [ {{ ''.join (('$','{', '#DEVICES[@]}')) }} -le 1 ] && echo \"No targets found, exiting ... \" && exit 1\n    select SDEVICE in \"${DEVICES[@]}\"\n    do\n        DEVICE=\"$(echo ${SDEVICE} | awk -F ' ' '{print $1}')\"\n        [ $SDEVICE == \"quit\" ] && echo \"User requested exit ...\" && exit 0\n        [ ! -z \"${DEVICE}\" ] && break\n    done\n}\n\nselect_device\n\nif [ ! -z $DEVICE ]\nthen\n    sed -i -r \"s#(UDPBD_DEVICE=).*#\\1/dev/${DEVICE}\\\"#\" /etc/systemd/system/ps2_udpbd.service\n    systemctl daemon-reload\nfi"
  },
  {
    "path": "ansible/templates/install_ps3netsrv/install_ps3netsrv.sh.j2",
    "content": "#!/bin/bash\n\nSRCDIR=\"{{ retronas_root }}/src\"\nBINDIR=\"{{ retronas_root }}/bin\"\n\n#echo \"Installing prerequisite packages...\"\n#apt install -y curl wget make gcc g++ automake autoconf build-essential unzip libmbedtls-dev libmbedtls12 meson ninja-build\n\necho \"Configuring build directories...\"\nrm -rf \"${SRCDIR}\"\nmkdir -p \"${SRCDIR}\"\ncd \"${SRCDIR}\"\n\necho \"Downloading ps3netsrv source code...\"\n\n#SOURCECODE=$( curl -kLs 'https://github.com/aldostools/webMAN-MOD/releases/latest' | awk -F '\"' '/href.*ps3netsrv.*zip/{print $2}'\n#SOURCECODE=$( curl -kLs 'https://github.com/aldostools/webMAN-MOD/tags' | awk -F '\"' /href.*${PATTERN}/'{print $4}' | head -n1 )\nSOURCECODE=$( curl -kLs https://api.github.com/repos/aldostools/webMAN-MOD/releases | jq -r \".[0].assets | map(select(.name | match (\\\"ps3netsrv\\\")))[-1] | .browser_download_url\" )\n[ -z \"$SOURCECODE\" ] && echo \"Couldn't get source code name\" && exit 1\n\necho $SOURCECODE\n#curl -OJL \"https://github.com/${SOURCECODE}\"\ncurl -OJL \"${SOURCECODE}\"\nunzip ps3netsrv*.zip\ncd ps3netsrv*/src\nmeson buildrelease --buildtype=release\nninja -C buildrelease\n\nif [ $? -eq 0 ]\nthen\n  echo \"Moving binary to RetroNAS bin dir...\"\n  mkdir -p \"${BINDIR}\" 2>/dev/null\n  mv -vf buildrelease/ps3netsrv \"${BINDIR}\"/\n\n  echo \"Cleaning up...\"\n  rm -rf \"${SRCDIR}\"\n\n  echo \"All done!\"\n  exit 0\nfi\n\necho \"Build failed! exiting\"\nexit 1\n"
  },
  {
    "path": "ansible/templates/install_ps3netsrv/ps3netsrv-perms.service.j2",
    "content": "[Unit]\nDescription=ps3netsrv for PlayStation3 permissions fix\n\n[Service]\nType=oneshot\nExecStart=/usr/bin/chown -RLc {{ retronas_user }}:{{ retronas_group }} \"{{ retronas_path }}/ps3/ps3netsrv\"\nExecStart=/usr/bin/chmod -Rc a-st,u+rwX,g+rwX,o+rX \"{{ retronas_path }}/roms/videos/\"\nExecStart=/usr/bin/chmod -Rc a-st,u+rwX,g+rwX,o+rX \"{{ retronas_path }}/roms/sony/\""
  },
  {
    "path": "ansible/templates/install_ps3netsrv/ps3netsrv-perms.timer.j2",
    "content": "[Unit]\nDescription=ps3netsrv for PlayStation3 permissions fix\n\n[Timer]\nOnUnitActiveSec=20s\nRandomizedDelaySec=15s\nPersistent=true\n\n[Install]\nWantedBy=timers.target"
  },
  {
    "path": "ansible/templates/install_ps3netsrv/ps3netsrv.service.j2",
    "content": "[Unit]\nDescription=ps3netsrv for PlayStation3 with CFW/HEN and webMAN-MOD\nAfter=network.target\nStartLimitIntervalSec=60\nStartLimitBurst=4\n\n[Service]\nUser={{ retronas_user }}\nExecStart={{ retronas_root }}/bin/ps3netsrv {{ retronas_path }}/ps3/ps3netsrv\nRestart=on-failure\nRestartSec=1\nSuccessExitStatus=3 4\nRestartForceExitStatus=3 4\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "ansible/templates/install_pygopherd/install_pygopherd.sh.j2",
    "content": "#!/bin/bash\n\nset -u\n\nAPP=pygopherd\nREPO=https://github.com/michael-lazar/${APP}.git\nOUTPATH=/opt\n\nfunction _log {\n\techo \"$1\"\n}\n\n### REQUIREMENTS\nREQFAIL=0\n[ $REQFAIL -ne 0 ] && _log \"Requirements failed, see previous errors\" && exit $REQFAIL\n\n\ncd $OUTPATH\nif [ ! -f ${OUTPATH}/${APP}/.git/config ] \nthen\n\tgit clone $REPO\nelse\n\tcd ${OUTPATH}/${APP}\n\tgit pull\nfi\n"
  },
  {
    "path": "ansible/templates/install_pygopherd/pygopherd.conf.j2",
    "content": "######################################################################\n# LOCAL DEVELOPMENT QUICKSTART SETTINGS\n######################################################################\n\n[pygopherd]\n\n##################################################\n# Operating System / Overall Behavior\n##################################################\n\n# Set this to true if you want the server to \"detach\" itself; that is,\n# to go into the background after it starts.\n\ndetach = no\n\n# If you want gopherd to write a PID file, set this to the location\n# where you want it.  Otherwise, comment out this line.\n\npidfile = /var/run/pygopherd.pid\n\n##################################################\n# Network\n##################################################\n\n# The server name to present to the world.  If you do not specify it\n# here, Pygopherd will attempt to figure it out automatically.\n# This is used only to present to clients.  It does not control\n# where the server listens.\n#\nservername = retronas\n\n# The interface on which to listen.  It should be an IP address or\n# a domain name.  If specified, pygopherd will attempt to listen to\n# the specified port on only this interface -- useful if you are doing\n# virtual hosting.  If not specified, pygopherd will listen on all\n# interfaces the OS provides.  If in doubt, do not specify this.\n#\n# interface = gopher.example.com\n\n# What port to listen on.  If not running as root, this must be\n# greater than 1024.\n\nport = 70\n\n# Type of server to run.  Valid options are ForkingTCPServer\n# and ThreadingTCPServer.  ForkingTCPServer is highly recommended\n# for now.\n\nservertype = ThreadingTCPServer\n\n# What port to *say* we're listening on.  Most people should NOT\n# specify this.  You might want to if you are using firewalling or\n# port forwarding and the port number is different to the world than\n# it is locally.\n\n# advertisedport = 70\n\n# Do we timeout on client conections?  HIGHLY RECOMMENDED!\n# Value is given in seconds.  If given, any read or write that makes\n# no progress in this number of seconds will time out.\n\ntimeout = 60\n\n##################################################\n# Data Handling\n##################################################\n\n# You can add a header to every directory by creating a\n# .abstract file in that directory and filling it with the\n# information you like.  This will then be rendered as a header\n# for the directory.  This option controls this feature.  Note:\n# for this to work, you must define a mapping to ABSTRACT in eaexts\n# below.\n\nabstract_headers = on\n\n# Individual files and folders can also have abstracts.  You can\n# choose to have pygopherd render these abstracts in the directory\n# listing itself -- they'll appear beneath the menu name for the file.\n# You can set this option to any of three values:\n#\n# always -- always render these abstracts.\n# unsupported -- render them only for protocols that do not natively\n# support abstracts.  Gopher+ is the only protocol that natively\n# supports them currently.\n# never -- never render these abstracts.\n\nabstract_entries = always\n\n##################################################\n# Error handling\n##################################################\n\n# If there is an error, you can decide whether or not to log a full\n# backtrace.  A full backtrace will usually be needed to find the\n# problem.\n\ntracebacks = yes\n\n##################################################\n# Security\n##################################################\n\n## Whether or not to use chroot.\n# This option is only valid if you are running pygopherd as root!\n\nusechroot = yes\n\n## Username and groupname to setreuid/setregid to.  Valid only if\n## starting pygopherd as root.  Comment out if you don't want this\n## functionality.  NOTE: DO NOT RUN AS ROOT UNLESS YOU USE THESE!  BAD BAD BAD!\n\nsetuid = {{ retronas_user }}\nsetgid = {{ retronas_group }}\n\n##################################################\n# TLS\n##################################################\n\n# Enable TLS by sniffing the first byte of the client request for the start of\n# a TLS handshake. This allows both TLS and plaintext requests to be served\n# over the same port.\n\nenable_tls = no\n\n# File paths to the server certificate and keyfile in PEM format. These are\n# required if TLS is enabled. When using chroot, the certificates will be\n# loaded while root privileges are still active.\n\ntls_certfile = ./testdata/demo.crt\ntls_keyfile = ./testdata/demo.key\n\n##################################################\n# Filesystem and MIME\n##################################################\n\n# Where the documents are stored.\n\nroot = {{ retronas_path }}\n\n# Location of a file to use to figure out MIME types.  You can\n# specify multiple files here -- just separate them with a colon.\n# ALL of them that are found will be read.\n\nmimetypes = ./conf/mime.types:/etc/pygopherd/mime.types:/etc/mime.types\n\n# Encodings.  You can use the default with the following syntax.  The\n# mimetypex.encodings_map is {'.Z': 'compress', '.gz': 'gzip'}.\n#\n# For ease of use in the config file, we specify this as a list of\n# tuples.  You can convert any hash to a list of tuples by using .items()\n\n# encoding = mimetypes.encodings_map.items()\n\n# You can override the default entirely (ie, to remove those) like this:\n\n# encoding = {'.bz2' : 'bzip2', '.gz' : 'gzip'}.items()\n# Or the same thing:\n# encoding = [('.bz2', 'bzip2'), ('.gz', 'gzip')]\n\n# Or, you can extend the default like so:\n\nencoding = list(mimetypes.encodings_map.items()) + \\\n          list({'.bz2' : 'bzip2',\n           '.tal': 'tal.TALFileHandler'\n          }.items())\n\n######################################################################\n# Logging\n######################################################################\n\n[logger]\n\n# Log method to use.  One of:\n#  syslog -- use Unix syslog facility\n#  file -- log to standard output (future capability for logging to other\n#          files)\n#  none   -- no logging\n\nlogmethod = file\n\n# If you enable syslog, you will need to define these as well:\n\n# priority -- one of the following (listed in order of high to low):\n# LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE,\n# LOG_INFO, LOG_DEBUG\n\npriority = LOG_INFO\n\n# Facility -- one of the following:\n# LOG_KERN, LOG_USER, LOG_MAIL, LOG_DAEMON, LOG_AUTH, LOG_LPR, LOG_NEWS,\n# LOG_UUCP, LOG_CRON, LOG_LOCAL0 - LOG_LOCAL7\n\nfacility = LOG_LOCAL3\n\n\n\n######################################################################\n# GOPHER OBJECTS\n######################################################################\n\n# Settings for gopher objects\n\n[GopherEntry]\n\n# Use this MIME type if no other type is found.\ndefaultmimetype = text/plain\n\n# Mapping from MIME types to gopher0 single-character types.\n# This is a list of lists.  The first entry in each list is a\n# regexp to match and the second is the result.\n# \n# Please have a .* at the end to map all unknown types to a certain\n# character.  For best results, that character should be nicely\n# corresponding to the defaultmimetype.\n\nmapping = [['text/html', 'h'],\n        ['text/.+', '0'],\n        ['application/mac-binhex40', '4'],\n        ['audio/.+', 's'],\n        ['image/gif', 'g'],\n        ['image/.+', 'I'],\n\t['application/gopher-menu', '1'],\n\t['application/gopher\\\\+-menu', '1'],\n        ['multipart/mixed', 'M'],\n        ['application/.+', '9'],\n        ['.*', '0'],\n        ]\n\n# This is used by the system to generate Extended Attribute (EA), aka\n# Gopher+ blocks for a file.  Basically, with the setup shown below,\n# you can create \"filename.txt.abstract\" to define the abstract\n# property for filename.txt.  If you do that, you will probably want\n# to add these properties to ignorepatt below.  The list is a mapping\n# from an extension to a block name.\n#\n# Sample for UMN compatibility:\n#\neaexts = {'.abstract' : 'ABSTRACT',\n          '.keywords' : 'KEYWORDS',\n          '.ask' : 'ASK',\n          '.3d' : '3D'}\n#\n# If you want to disable this capability, use this:\n#\n\n# eaexts = {}\n\n\n######################################################################\n# HANDLERS\n######################################################################\n\n##################################################\n# Handler multiplexer\n##################################################\n\n[handlers.HandlerMultiplexer]\n\n# A list of the handlers to consider.  The handlers\n# are tried in the order listed.\n#\n#\n### Suggested settings:\n# Note: the UMNDirHandler will handle all directories, even if they\n# do not have UMN-specific files, so you do not need to list the\n# dirhandler in this case.\n#\n# Warning: scriptexec and pyg can execute arbitrary code stored in\n# your path.  Don't enable unless you know what you're doing!\n#\n# For UMN emulation:  (full UMN featureset excluding scriptexec, no others)\n#\n# handlers = [url.HTMLURLHandler, UMN.UMNDirHandler,\n#             html.HTMLFileTitleHandler, \n#             mbox.MBoxMessageHandler, mbox.MBoxFolderHandler,\n#             file.CompressedFileHandler, file.FileHandler]\n#\n# For Bucktooth emulation:  (full Buck featureset excluding scriptexec)\n#\n#handlers = [gophermap.BuckGophermapHandler, url.HTMLURLHandler,\n#             file.FileHandler, dir.DirHandler]\n#\n# For full Pygopherd featureset excluding scripts, compression, and PYG.\n# Supports both UMN and Bucktooth featuresets.  This is the default\n# configuration for Pygopherd because it is secure yet versatile.\n#\n\n# handlers = [url.HTMLURLHandler, gophermap.BuckGophermapHandler,\n#             mbox.MaildirFolderHandler, mbox.MaildirMessageHandler,\n#             UMN.UMNDirHandler, html.HTMLFileTitleHandler,\n#             mbox.MBoxMessageHandler, mbox.MBoxFolderHandler,\n#             file.FileHandler]\n\n# For full Pygopherd featureset including scripts and PYG.  Same as\n# above but adds scripts, decompression, and PYG execution.\n\nhandlers = [url.HTMLURLHandler, gophermap.BuckGophermapHandler,\n            mbox.MaildirFolderHandler, mbox.MaildirMessageHandler,\n            UMN.UMNDirHandler,\n            tal.TALFileHandler,\n            html.HTMLFileTitleHandler,\n            mbox.MBoxMessageHandler, mbox.MBoxFolderHandler,\n            pyg.PYGHandler, scriptexec.ExecHandler,\n            ZIP.ZIPHandler,\n            file.CompressedFileHandler, file.FileHandler,\n            url.URLTypeRewriter]\n\n##################################################\n# Decompressing file handler\n##################################################\n\n[handlers.file.CompressedFileHandler]\n\n# Decompressors is a map from an encoding (as specified in the\n# pygopherd section above) to a decompression program.\n# The decompression program must\n# accept the input in its stdin and write the decompressed output\n# to stdout.\n#\n# If you do not want to decompress things automatically for your\n# clients, you might wish to NOT use this handler.\n#\n# Note: this feature is probably NOT compatible with chroot unless\n# you take extra precautions.\n\n# We enable no decompressors by default... you'll need to do that.\n\n# decompressors = {}\n\ndecompressors = {'bzip2': 'bzcat',\n              'gzip' : 'zcat',\n              'compress' : 'zcat'}\n\n# Regexp to match against filenames pending decompression.\n# The default will let ALL files be decompressed.\n\ndecompresspatt = .*\n\n# You can be more restrictive:\n\n# decompresspatt = \\.txt\\.(bz2|gz|Z)$\n\n\n##################################################\n# Directory handler\n##################################################\n           \n[handlers.dir.DirHandler]\n# A regular expression of files to ignore.  These files\n# will not be presented in lists of files to clients,\n# but if clients know the exact path to the files, they can\n# still be requested.\n#\n# This pattern is matched against the requested selector.\n# Selectors are guaranteed to begin with a slash by this point.\n# and never end with a slash unless they consist solely of a slash.\n#\n# By default, we ignore files starting with a period, gophermap\n# files, and files ending with a tilde.\n#\n# The default emulates UMN's default plus buck.  Please note:\n# UMNDirHandler implicitly will keep all files starting with a dot out of\n# directory listings.  If you exclude these files explicitly in ignorepatt,\n# then not only will they not show up, but the handler will also not scan\n# them for links and the like.\n#\n# A buck-only server might like:\n#\n# ignorepatt = ~$|/\\.|/gophermap$\n\nignorepatt = /.cap$|/lost\\+found$|/lib$|/bin$|/etc$|/dev$|~$|/\\.cache|/\\.forward$|/\\.message$|/\\.hushlogin$|/\\.kermrc$|/\\.notar$|/\\.where$|/veronica.ctl$|/robots.txt$|/nohup.out$|/gophermap$|\\.abstract$|\\.keyboards$|\\.ask|\\.3d$|~$\n\n# Expiration time, in seconds, for the cache.\n# Set to 0 to disable caching entirely.\n\ncachetime = 0\n\n# Name of the cahe file.  Must be set to something even if the cachetime\n# is zero.  In that case, this filename will not be used but for the conf\n# file to parse, it must still be set.\n\ncachefile = .cache.pygopherd.dir\n\n##################################################\n# UMN Directory Handler\n##################################################\n\n[handlers.UMN.UMNDirHandler]\n\n# Extension stripping behavior.  When a file from a directory\n# is presented in a menu, and no name is given, what to do?\n# For instance, given a file Welcome.txt and pygopherd.tar.gz:\n#\n# If extstrip is none, present Welcome.txt and pygopherd.tar.gz in the\n# menu.\n#\n# If extstrip is nonencoded, modify only those files that do not\n# have encodings.  (If CompressedFileHandler is used, modify only\n# those files that to not have *HANDLED* encodings.)\n# If gzip is NOT a handled encoding, you'd get names Welcome and\n# pygopherd.tar.gz.  If gzip IS a handled encoding, you'd get\n# Welcome and pygopherd.\n#\n# If extstrip is full, modify all modifyable names.  Welcome.txt ->\n# Welcome and pygopherd.tar.gz -> pygopherd.\n\n# extstrip = none\nextstrip = nonencoded\n# extstrip = full\n\n[handlers.ZIP.ZIPHandler]\n##################################################\n# ZIP file handler\n##################################################\n\n# Even if it's listed in the available handlers, it's disabled here by\n# default.\n\nenabled = true\n\npattern = \\.zip$\n\n######################################################################\n# PROTOCOLS\n######################################################################\n\n##################################################\n# Protocol Multiplexer\n##################################################\n\n[protocols.ProtocolMultiplexer]\n\n# A list of the protocols to consider for each request.\n# The protocols are tried in the order listed.\n\nprotocols = [wap.WAPProtocol, gemini.GeminiProtocol,\n             http.HTTPProtocol, http.HTTPSProtocol,\n             spartan.SpartanProtocol,\n             gopherp.GopherPlusProtocol, gopherp.SecureGopherPlusProtocol,\n             rfc1436.GopherProtocol, rfc1436.SecureGopherProtocol]\n\n##################################################\n# Gopher+ Protocol\n##################################################\n\n[protocols.gopherp.GopherPlusProtocol]\n# The name and e-mail of the administrator\nadmin = retronas <retronas@retro.nas>\n\n##################################################\n# HTTP Protocol\n##################################################\n\n[protocols.http.HTTPProtocol]\niconmapping = {'h' : 'text.gif',\n            '0' : 'text.gif',\n            '4' : 'binhex.gif',\n            's' : 'sound1.gif',\n            'g' : 'image3.gif',\n            'I' : 'image3.gif',\n            'M' : 'text.gif',\n            '9' : 'binary.gif',\n            '1' : 'folder.gif',\n            '7' : 'folder.gif',\n            'i' : 'blank.gif'}\n\n# You can use this option to put something at the top of each HTML\n# page generated.\n# \n# The following tokens will be interpolated:\n#\n# GOPHERURL          -- the Gopher URL for this page.\n#\n\npagetopper = Welcome to RetroNAS!  You are browsing Gopher through\n   a Web interface right now.  You can use most browsers or Gopher\n   clients to browse Gopher natively.  If your browser supports it,\n   <A HREF=\"GOPHERURL\">try clicking here</A> to see this page\n   in Gopher directly.  To find Gopher browsers,\n   <A HREF=\"https://en.wikipedia.org/wiki/Gopher_(protocol)#Client_software\">click\n   here</A>.<HR>\n\n##################################################\n# WAP Protocol\n##################################################\n\n[protocols.wap.WAPProtocol]\n\n# waptop is the URL to access with WAP devices.  The default, /wap, means\n# that accessing http://sitename.com/wap will bring up your site in WAP\n# mode.\n#\n# PyGopherd can autodetect WAP from some phones, so this is not always\n# necessary.\n\nwaptop = /wap\n\n##################################################\n# Gemini Protocol\n##################################################\n\n[protocols.gemini.GeminiProtocol]\n# Note that the gemini protocol *requires* the TLS section to also be enabled.\n\nfooter = => https://www.github.com/michael-lazar/pygopherd Generated by PyGopherd\n\n##################################################\n# Spartan Protocol\n##################################################\n\n[protocols.gemini.SpartanProtocol]\n\nfooter = => https://www.github.com/michael-lazar/pygopherd Generated by PyGopherd"
  },
  {
    "path": "ansible/templates/install_pygopherd/pygopherd.service.j2",
    "content": "[Unit]\nDescription=Pygopherd\nAfter=multi-user.target\n\n[Service]\nType=simple\nRestart=always\nWorkingDirectory=/opt/{{ my_name }}\nEnvironment=PYTHONPATH=/opt/{{ my_name }}\nExecStart=/opt/pygopherd/bin/{{ my_name }} {{ retronas_root}}/etc/{{ my_name }}.conf\nTimeoutStartSec=0\nRemainAfterExit=yes\n\n[Install]\nWantedBy=multi-user.target"
  },
  {
    "path": "ansible/templates/install_rclone/rclone-webui.service.j2",
    "content": "﻿[Unit]\nDescription=rclone webui\nRequires=network-online.target\nAfter=network-online.target\n\n[Service]\nType=simple\nUser={{ retronas_user }}\nExecStart=/usr/bin/rclone rcd --rc-web-gui --rc-web-gui-no-open-browser --rc-addr 0.0.0.0:5572 --rc-htpasswd /etc/retronas.htpasswd\nTimeoutStopSec=10\nRestart=on-failure\n\n[Install]\nWantedBy=default.target\n"
  },
  {
    "path": "ansible/templates/install_redumper/install_redumper.sh.j2",
    "content": "#!/bin/bash\n\n###\n# The x86_64 version is pulled from the official github\n# The aarch64 version is manually built and hosted seperately\n#\n\nset -e\n\nAPP=redumper\nDESTDIR=/opt/${APP}\n\n[ ! -d \"${DESTDIR}\" ] && mkdir -p \"${DESTDIR}\"\n\ndl_install() {\n    RELEASE=${1}\n    \n    [ -z \"$RELEASE\" ] && echo \"Couldn't get release \" && exit 1\n    RTMP=$(mktemp -d)\n    cd $RTMP\n    curl -sOJL \"${RELEASE}\"\n    unzip $(basename ${RELEASE})\n    mv redumper*Linux*/bin/* $DESTDIR\n    cd ..\n    rm -rf $RTMP\n}\n\nget_x86_64() {\n    ORG=superg\n    REPO=redumper\n    RELEASE=$(curl -skJL https://api.github.com/repos/${ORG}/${REPO}/releases | jq -r \".[0].assets | map(select(.name | match (\\\"Linux\\\")))[-1] | .browser_download_url\" )\n    dl_install $RELEASE\n}\n\nget_aarch64() {\n    URL=https://mameau.com/notes/retronas/redumper/aarch64/\n    PATTERN=\"redumper-build*linux-aarch64.zip\"\n    RELEASE=$(curl -skJL $URL | sed -rn \"s/^.*($PATTERN)\\\">.*/\\1/p\" | sort -r | head -n1)\n    dl_install $URL/$RELEASE\n}\n\ncase $(uname -m) in\n    x86_64)\n        get_x86_64\n        ;;\n\n    aarch64)\n        get_aarch64\n        ;;\nesac"
  },
  {
    "path": "ansible/templates/install_retroaimserver/install_retroaimserver.sh.j2",
    "content": "#!/bin/sh\n\nset -x\n\nPAT_ARM=\"linux.arm64_.*tar.gz\"\nPAT_X86=\"linux.x86_64.tar.gz\"\nDEST=/opt/retro-aim-server\nDESTFILE=retro-aim-server.tar.gz\n\ncase $(uname -m) in\n\taarch64)\n\t\tPAT=$PAT_ARM\n\t;;\n\tx86_64)\n\t\tPAT=$PAT_X86\n\t;;\n\t*)\n\techo \"Unknown architecture $(uname -m)\"\nesac\n\nRELEASE=$( curl -kLs https://api.github.com/repos/mk6i/retro-aim-server/releases | jq -r \".[0].assets | map(select(.name | match (\\\"${PAT}\\\")))[-1] | .browser_download_url\" )\n\nif [ ! -z $RELEASE ]\nthen\n\tcurl -JLo $TMPDIR/$DESTFILE \"${RELEASE}\"\nfi\n\n[ ! -d $DEST ] && mkdir -p $DEST\n\nif [ -f $TMPDIR/$DESTFILE ]\nthen\n\ttar -xvf $TMPDIR/$DESTFILE -C $DEST --strip=1\nfi\n\nchown -R {{ retronas_user }}:{{ retronas_group }} $DEST\nchmod +x $DEST/retro_aim_server"
  },
  {
    "path": "ansible/templates/install_retroaimserver/retro-aim-server.service.j2",
    "content": "[Unit]\nDescription=retro-aim-server\nAfter=network.target\nStartLimitIntervalSec=60\nStartLimitBurst=4\n\n[Service]\nUser={{ retronas_user }}\nWorkingDirectory=/opt/retro-aim-server\nExecStart=/opt/retro-aim-server/retro_aim_server\nRestart=on-failure\nRestartSec=5\nSuccessExitStatus=3 4\nRestartForceExitStatus=3 4\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "ansible/templates/install_retroaimserver/retroaimserver.xml.j2",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<service>\n  <port port=\"8080\" protocol=\"tcp\"/>\n  <port port=\"5194\" protocol=\"tcp\"/>\n  <port port=\"5190\" protocol=\"tcp\"/>\n  <port port=\"5195\" protocol=\"tcp\"/>\n  <port port=\"5191\" protocol=\"tcp\"/>\n  <port port=\"5193\" protocol=\"tcp\"/>\n  <port port=\"5912\" protocol=\"tcp\"/>\n  <port port=\"5196\" protocol=\"tcp\"/>\n  <port port=\"5197\" protocol=\"tcp\"/>\n</service>"
  },
  {
    "path": "ansible/templates/install_retroaimserver/retroainserver.xml.j2",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<service>\n  <port port=\"8080\" protocol=\"tcp\"/>\n  <port port=\"5194\" protocol=\"tcp\"/>\n  <port port=\"5190\" protocol=\"tcp\"/>\n  <port port=\"5195\" protocol=\"tcp\"/>\n  <port port=\"5191\" protocol=\"tcp\"/>\n  <port port=\"5193\" protocol=\"tcp\"/>\n  <port port=\"5912\" protocol=\"tcp\"/>\n  <port port=\"5196\" protocol=\"tcp\"/>\n  <port port=\"5197\" protocol=\"tcp\"/>\n</service>"
  },
  {
    "path": "ansible/templates/install_romimport/romimport.sh.j2",
    "content": "#!/bin/bash\n\nset -u\n\nTDIR=\"{{retronas_root}}/bin/Hardware-Target-Game-Database\"\nMANIPULATE=0\nFORCE=0\nXTERMINATE=0\nTARGET=\"\"\n\ndeclare -A SYSTEMS\n\nSYSTEMS=(\n  {% for item in system_map if item.smdb %}\n  [{{ item.src }}]=\"{{ item.smdb }}\"\n  {% endfor %}\n)\n\n\n## REQUIREMENTS\n[ ! -d \"${TDIR}\" ] &&  echo \"Cannot locate ROM import SMBD directory. Please add it from the Install Things menu.\" && exit 1\n\n## hacky af\nlist_targets() {\n  echo \"Use id with -t to run a single system\"\n  echo \"-------------------------------------\"\n\n  ### dump to screen\n  for SYSTEM in ${!SYSTEMS[@]}\n  do\n    echo \"id: ${SYSTEM}, ${SYSTEMS[$SYSTEM]}\"\n  done | column -s\",\" -t\n  exit 0\n}\n\n_usage() {\n  echo \"Usage $0\" \n  echo \"-h this help\"\n  echo \"-t target system\"\n  echo \"-l list available targets\"\n  exit 0\n}\n\nOPTSTRING=\"hlt:\"\nwhile getopts $OPTSTRING ARG\ndo\n  case $ARG in\n    h)\n      _usage\n      ;;\n    l)\n      # list targets\n      list_targets\n      ;;\n    t)\n      TARGET=${OPTARG}\n      ;;\n  esac\ndone\n\nrun_buildpack() {\n  IFS=$'\\n'\n  local ODIR=\"${1}\"\n  local SMDB=\"${2}\"\n  local MSDB=\"$(basename $(echo $SMDB) .txt)\"\n\n  # UNARMED FOR TESTING\n  cd \"${TDIR}\"\n  python3 build_pack.py --input_folder \"{{retronas_path}}/romimport\" --output_folder \"${ODIR}\" --database \"${SMDB}\" --file_strategy smart --skip_existing --drop_initial_directory --missing \"{{retronas_path}}/romimport/${MSDB}_missing.txt\"\n\n}\n\nupdateSMDB() {\n  cd ${TDIR}\n  echo \"Refreshing SMBDs...\"\n  umask 0002\n  git config pull.rebase false\n  git reset --hard HEAD\n  git pull\n}\n\nauditsystem() {\n\n  local SYSTEM=\"${1:-IAMNOTFOUND}\"\n\n  if [ ${SYSTEMS[${SYSTEM}]+_} ]\n  then\n    ODIR=\"{{retronas_path}}/roms/${SYSTEM}\"\n    SMDB=\"./EverDrive Pack SMDBs/${SYSTEMS[${SYSTEM}]}\"\n\n    if [ -d \"${ODIR}\" ]\n    then \n      echo '------------------------'\n      echo \"Analysing with ${SMDB}\"\n      echo \"Outputting to ${ODIR}\"\n      run_buildpack \"${ODIR}\" \"${SMDB}\"\n      echo -e '------------------------\\n'\n    else\n      echo \"Failed to file output directory ${ODIR}\"\n    fi\n  else \n    _usage\n  fi\n}\n\nif [ -z \"${TARGET}\" ]\nthen\n  updateSMDB\n  for SYSTEM in ${!SYSTEMS[@]}\n  do\n    auditsystem ${SYSTEM}\n  done\nelse\n  updateSMDB\n  auditsystem ${TARGET}\nfi\n"
  },
  {
    "path": "ansible/templates/install_romm_cifs/retronas-romm-dirs.service.j2",
    "content": "[Unit]\nDescription=retronas-romm-dirs\n\n[Service]\nType=simple\nRestart=no\nExecStart={{ retronas_root }}/scripts/retronas-romm-dirs.sh\nTimeoutStartSec=0\nRemainAfterExit=no\n"
  },
  {
    "path": "ansible/templates/install_romm_cifs/retronas-romm-dirs.sh.j2",
    "content": "#!/bin/sh\n\ncd /opt/retronas/ansible\n/usr/bin/git reset --hard HEAD\n/usr/bin/git pull\n/usr/bin/ansible-playbook {{ my_file }}.yml\n\n"
  },
  {
    "path": "ansible/templates/install_romm_cifs/retronas-romm-dirs.timer.j2",
    "content": "[Unit]\nDescription=Run retronas-romm-dirs\n\n[Timer]\nOnCalendar=daily\nPersistent=true\n\n[Install]\nWantedBy=timers.target"
  },
  {
    "path": "ansible/templates/install_sabretools/install_sabretools.sh.j2",
    "content": "#!/bin/bash\nset -eu\n\nAPP=SabreTools\nORG=$APP\nREPO=$APP\nDESTDIR=/opt/${APP}\n\n\ncase $(uname -m) in\n    x86_64)\n        ARCH=x64\n        ;;\n\n    aarch64)\n        ARCH=arm64\n        ;;\n    *)\n      echo \"Unsupported arch\"\n      exit 1\nesac\n\ndl_install() {\n    RELEASE=${1}\n    \n    [ -z \"$RELEASE\" ] && echo \"Couldn't get release \" && exit 1\n    [ \"$RELEASE\" == \"null\" ] && echo \"Couldn't get release \" && exit 1\n    [ ! -d \"${DESTDIR}\" ] && mkdir -p \"${DESTDIR}\"\n\n    RTMP=$(mktemp -d)\n    cd $RTMP\n    curl -sOJL \"${RELEASE}\"\n\n    FILENAME=$(basename ${RELEASE})\n    if unzip -o $FILENAME -d $DESTDIR\n    then\n      [ -f /usr/local/bin/sabretools ] && rm -f /usr/local/bin/sabretools\n      ln -s ${DESTDIR}/${APP} /usr/local/bin/sabretools\n      rm -rf $RTMP\n    fi\n}\n\nRELEASE=$(curl -skJL https://api.github.com/repos/${ORG}/${REPO}/releases | jq -r \".[0].assets | map(select(.name | match (\\\"linux-${ARCH}_release\\\")))[-1] | .browser_download_url\" )\ndl_install $RELEASE\n"
  },
  {
    "path": "ansible/templates/install_seaweedfs/install_seaweedfs.sh.j2",
    "content": "#!/bin/bash\n\nBINDIR=\"{{ retronas_root }}/bin\"\n\necho \"Downloading seaweedfs...\"\nTARFILE=linux_$(dpkg --print-architecture).tar.gz\n\nRELEASE=$( curl -kLs https://api.github.com/repos/seaweedfs/seaweedfs/releases | jq -r \".[0].assets | map(select(.name | match (\\\"^${TARFILE}$\\\")))[-1] | .browser_download_url\" )\n[ -z \"$RELEASE\" ] && echo \"Couldn't get release\" && exit 1\n\necho $RELEASE\ncd /tmp\ncurl -ksOJL \"${RELEASE}\"\ntar xvf ${TARFILE}\n\necho \"Moving binary to RetroNAS bin dir...\"\nmkdir -p \"${BINDIR}\" 2>/dev/null\nmv -vf weed \"${BINDIR}\"/\n\necho \"Cleaning up...\"\nrm -f \"/tmp/${RELEASE}\"\n\necho \"All done!\"\n"
  },
  {
    "path": "ansible/templates/install_seaweedfs/seaweedfs-credentials.sh.j2",
    "content": "#!/bin/bash\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\nWEEDCONF=${1:-/opt/retronas/bin/weed-retronas-s3.json}\nMATCH=${2:-retronas}\n\nif [ -f ${WEEDCONF} ]\nthen\n    echo \"Credentials for the identity: ${MATCH}\"\n    jq -r \".[][] | select(.name == \\\"${MATCH}\\\").credentials[]\" ${WEEDCONF}\nelse\n    echo \"Config file not found: ${WEEDCONF}\"\nfi\n\nPAUSE"
  },
  {
    "path": "ansible/templates/install_seaweedfs/seaweedfs-retronas.service.j2",
    "content": "[Unit]\nDescription=seaweedfs s3/filer/volume storage\nAfter=network.target\nStartLimitIntervalSec=60\nStartLimitBurst=4\n\n[Service]\nExecStart={{ retronas_root }}/bin/weed server --dir={{ retronas_path }}/s3 -dataCenter=retronas -master -master.defaultReplication=000 -master.volumeSizeLimitMB=10000 -volume -volume.max=1 -s3 -s3.config={{ retronas_root }}/bin/weed-retronas-s3.json\nRestart=on-failure\nRestartSec=1\nSuccessExitStatus=3 4\nRestartForceExitStatus=3 4\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "ansible/templates/install_seaweedfs/seaweedfs.xml.j2",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<service>\n  # master\n  <port port=\"9333\" protocol=\"tcp\"/>\n  <port port=\"19333\" protocol=\"tcp\"/>\n  # volume\n  <port port=\"8080\" protocol=\"tcp\"/>\n  <port port=\"18080\" protocol=\"tcp\"/>\n  # filer\n  <port port=\"8888\" protocol=\"tcp\"/>\n  <port port=\"18888\" protocol=\"tcp\"/>\n  # s3\n  <port port=\"8333\" protocol=\"tcp\"/>\n</service>"
  },
  {
    "path": "ansible/templates/install_seaweedfs/weed-retronas-s3.json.j2",
    "content": "{\n  \"identities\": [\n    {\n      \"name\": \"retronas\",\n      \"credentials\": [\n        {\n          \"accessKey\": \"{{ retronas_s3_access_key.stdout }}\",\n          \"secretKey\": \"{{ retronas_s3_secret_key.stdout }}\"\n        }\n      ],\n      \"actions\": [\n        \"Admin\",\n        \"Read\",\n        \"ReadAcp\",\n        \"List\",\n        \"Tagging\",\n        \"Write\",\n        \"WriteAcp\"\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "ansible/templates/install_sit/install_sit.sh.j2",
    "content": "#!/bin/bash\n\nset -u\n\nRN_BIN=/usr/local/bin/\nSRCDIR={{ retronas_root }}/src\nREPO=sit\nOWNER=thecloudexpanse\n\nXC=(\n  sit\n  macbinfilt\n)\n\n[ ! -d $SRCDIR ] && mkdir $SRCDIR\n\ncd $SRCDIR\ngit clone https://github.com/${OWNER}/${REPO}\ncd ${REPO}\n\nmake\n\nfor X in ${XC[@]}\ndo\n  [ ! -f ${X} ] && echo \"${X} not found\" && exit 1\n  chmod +x ${X}\n  mv ${X} ${RN_BIN}/\ndone\n\n# CLEAN UP\nrm -Rf ${SRCDIR}/\n"
  },
  {
    "path": "ansible/templates/install_smbmounter/retronas_smbmounter.conf.j2",
    "content": "## RetroNAS config.\n## This is autogenerated. Changes will be lost.\ncomment = Amiga SMBMounter\npath = {{ retronas_path }}/amiga\nguest ok = no\nbrowseable = yes\nwrite list = {{ retronas_user }}\nwriteable = yes\nvalid users = {{ retronas_user }}\ncreate mask = 0775\ndirectory mask = 0775\nfollow symlinks = yes\nwide links = yes\nstrict sync = no"
  },
  {
    "path": "ansible/templates/install_tcpser/install_tcpser.sh.j2",
    "content": "#!/bin/bash\n\nset -u\n\nSRCDIR=\"{{ retronas_root }}/src\"\nBINDIR=\"{{ retronas_root }}/bin\"\nREPO=https://github.com/FozzTexx/tcpser.git\n\n\nif [ ! -x \"${BINDIR}/tcpser\" ]\nthen\n    echo \"tcpser not found, so we'll build it!\"\n\n    echo \"Configuring build directories...\"\n    [ -d ${SRCDIR} ] && rm -rf \"${SRCDIR}\"\n    mkdir -p \"${SRCDIR}\"\n    cd \"${SRCDIR}\"\n\n    echo \"Downloading git source ...\"\n    git clone $REPO\n    cd \"${SRCDIR}/$(basename $REPO .git)\"\n\n    echo \"Patching Makefile to build for pthread\"\n    sed -i 's/^LDFLAGS\\s=.*$/LDFLAGS = -pthread -lpthread/' Makefile\n\n    echo \"Building\"\n    make\n\n    echo \"Moving binary to RetroNAS bin dir...\"\n    mkdir -p \"${BINDIR}\" 2>/dev/null\n    mv -vf tcpser \"${BINDIR}/\"\n\n    echo \"Cleaning up...\"\n    [ -d ${SRCDIR} ] && rm -rf \"${SRCDIR}\"\n\n    echo \"All done!\"\nfi\n\n"
  },
  {
    "path": "ansible/templates/install_tcpser/tcpser@.service.j2",
    "content": "[Unit]\nDescription=tcpser %i\nAfter=network.target\nStartLimitIntervalSec=60\nStartLimitBurst=4\n\n[Service]\nUser={{ retronas_user }}\nEnvironmentFile={{ retronas_root }}/etc/tcpser/tcpser-%i\nExecStart={{ retronas_root }}/bin/tcpser ${DEVICE} ${SPEED} ${LISTEN} ${ADDN}\nRestart=on-failure\nRestartSec=1\nSuccessExitStatus=3 4\nRestartForceExitStatus=3 4\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "ansible/templates/install_telnet/telnet.j2",
    "content": "service telnet\n{\n\tflags = REUSE\n\tsocket_type = stream\n\twait = no\n\tuser = root\n\tserver = /usr/sbin/telnetd\n\tlog_on_failure += USERID\n\tdisable = no\n}"
  },
  {
    "path": "ansible/templates/install_tftpd-hpa/tftpd-hpa.j2",
    "content": "# /etc/default/tftpd-hpa\n\nTFTP_USERNAME=\"{{ retronas_user }}\"\nTFTP_DIRECTORY=\"{{ retronas_path }}\"\nTFTP_ADDRESS=\":69\"\nTFTP_OPTIONS=\"--secure --create --permissive --umask 0000\"\n"
  },
  {
    "path": "ansible/templates/install_tnfs/install_tnfs.sh.j2",
    "content": "#!/bin/bash\n\n# Set world readable/executable umask\numask 0022\n\n# Set the install dir\nIDIR=\"{{ retronas_root }}/bin/tnfs\"\nmkdir -p \"${IDIR}\"\n\n# make/clean the source build location\nmkdir -p \"{{ retronas_root }}/src\"\ncd \"{{ retronas_root }}/src\"\nrm -rf spectranet\n\n# Clone the source\ngit clone https://github.com/FujiNetWIFI/spectranet.git\ncd spectranet/tnfs/tnfsd\n\n# Build and install\nrm -rvf bin/*\nmake OS=LINUX\ncp -vf bin/tnfsd \"${IDIR}/\"\n"
  },
  {
    "path": "ansible/templates/install_tnfs/tnfsd.service.j2",
    "content": "﻿[Unit]\nDescription=TNFS for Atari 8-bit and ZX Spectrum\nDocumentation=https://github.com/FujiNetWIFI/spectranet\nRequires=network-online.target\nAfter=network-online.target\n\n[Service]\nType=simple\nExecStart={{ retronas_root }}/bin/tnfs/tnfsd {{ retronas_path }} -c {{ retronas_user }}\nTimeoutStopSec=10\nRestart=on-failure\n\n[Install]\nWantedBy=default.target\n"
  },
  {
    "path": "ansible/templates/install_ucon64/install_ucon64.sh.j2",
    "content": "#!/bin/bash\n\nset -u\n\nRN_BIN=/usr/local/bin/\n\nVERSION=2.2.2\nOUTPATH=/tmp/ucon64\nOUTFILE=ucon64-${VERSION}-src.tar.gz\nURL=https://sourceforge.net/projects/ucon64/files/ucon64/ucon64-${VERSION}\nXC=ucon64\nCONFIGURE_ARGS=\"\"\n\nfunction _log {\n\techo \"$1\"\n}\n\n### REQUIREMENTS\nREQFAIL=0\n[ ! -x /usr/bin/gmake ] && _log \"GMAKE not found\" && REQFAIL=1\n[ $REQFAIL -ne 0 ] && _log \"Requirements failed, see previous errors\" && exit $REQFAIL\n\n### MAKE directory\n_log \"Checking for ${OUTPATH}\"\n[ ! -d ${OUTPATH} ] && mkdir -p \"${OUTPATH}\"\n[ -f ${OUTPATH}/${OUTFILE} ] && rm -f ${OUTPATH}/${OUTFILE}\n\n### DOWNLOAD ucon64\nRESULT=\"FAILURE\"\n_log \"Attempting to download ucon64 ${VERSION} from ${URL}\"\ncurl -sqL -o${OUTPATH}/${OUTFILE} \"${URL}/${OUTFILE}/\"\n[ $? -eq 0 ] && RESULT=\"SUCCESS\"\n_log \"Download result: ${RESULT}\"\n\n### UNPACK\nif [ -f \"${OUTPATH}/${OUTFILE}\" ]\nthen\n\tcd ${OUTPATH}\n\ttar xzvf ${OUTFILE}\n  cd ucon64-${VERSION}-src/src/\nfi\n\n### CHECK parallel port support\nPARALLEL_H=/usr/include/x86_64-linux-gnu/sys/io.h\n\n# RPI is missing sys/io.h headers so parallel devices can't be supported (part of libc6-dev)\n[ ! -f ${PARALLEL_H} ] && CONFIGURE_ARGS+=\"--disable-parallel \"\n\n# CONFIGURE\n./configure ${CONFIGURE_ARGS}\n\n# MAKE\ngmake\n\n[ -x ${XC} ] && mv ${XC} \"${RN_BIN}/\"\nchmod 755 ${RN_BIN}/${XC}\n\n# CLEAN UP\nrm -Rf ${OUTPATH}/\n"
  },
  {
    "path": "ansible/templates/install_waybackproxy/config.json.j2",
    "content": "{\n    \"LISTEN_PORT\": 8888,\n    \"DATE\": \"20011025\",\n    \"DATE_TOLERANCE\": 365,\n    \"GEOCITIES_FIX\": true,\n    \"QUICK_IMAGES\": true,\n    \"WAYBACK_API\": true,\n    \"CONTENT_TYPE_ENCODING\": true,\n    \"SILENT\": false,\n    \"SETTINGS_PAGE\": true\n}"
  },
  {
    "path": "ansible/templates/install_waybackproxy/install_waybackproxy.sh.j2",
    "content": "#!/bin/bash\n\n# Set world readable/executable umask\numask 0022\n\n# Clone the source\ncd /opt\ngit clone https://github.com/richardg867/WaybackProxy {{ my_name }}"
  },
  {
    "path": "ansible/templates/install_waybackproxy/waybackproxy.service.j2",
    "content": "[Unit]\nDescription=WaybackProxy\nDocumentation=https://github.com/richardg867/WaybackProxy\nRequires=network-online.target\nAfter=network-online.target\n\n[Service]\nType=simple\nWorkingDirectory=/opt/{{ my_name }}/\nExecStart=/usr/bin/python3 /opt/{{ my_name }}/{{ my_name }}.py\nTimeoutStopSec=10\nRestart=on-failure\n\n[Install]\nWantedBy=default.target"
  },
  {
    "path": "ansible/templates/install_webone/install_webone.sh.j2",
    "content": "#!/bin/bash\n\nDTVER=8\nexport PATH={{ retronas_root }}/bin/dotnetcore${DTVER}:{{ retronas_root }}/bin/dotnetcore${DTVER}/tools:${PATH}\nexport DOTNET_ROOT={{ retronas_root }}/bin/dotnetcore${DTVER}\nexport DOTNET_INSTALL_DIR={{ retronas_root }}/bin/dotnetcore${DTVER}\n\nSRCDIR=\"{{ retronas_root }}/src\"\nBINDIR=\"{{ retronas_root }}/bin\"\n\nrm -rf \"${SRCDIR}\"\nmkdir -p \"${SRCDIR}\"\ncd \"${SRCDIR}\"\n\n# Clone the WebOne repo\ngit clone https://github.com/atauenis/webone.git\n\ncd webone\n\n# Fix a case-sensitive file issue\nsed -i 's/webone.csproj/WebOne.csproj/g' WebOne.sln\n\n# Install the dotnet-tarball tool\ndotnet tool install --global dotnet-tarball\n\n# Follow build guide from WebOne README.md\ndotnet restore\ndotnet publish -c Release\ndotnet build -c Release\n\n# Move the binaries into the bindir\nmkdir -p \"${BINDIR}/webone\"\nmv -vf ${SRCDIR}/webone/bin/Release/net${DTVER}.0/* \"${BINDIR}/webone/\"\n\n# Cleanup\nrm -rf \"${SRCDIR}\"\n"
  },
  {
    "path": "ansible/templates/install_webone/webone.service.j2",
    "content": "﻿[Unit]\nDescription=WebOne HTTP Proxy Server\nDocumentation=https://github.com/atauenis/webone/wiki/\nRequires=network-online.target\nAfter=network-online.target\n\n[Service]\nType=simple\n#DynamicUser=yes\nEnvironment=\"HOME=/tmp/\"\nEnvironment=\"DOTNET_ROOT={{ retronas_root }}/bin/dotnetcore8\"\nEnvironment=\"DOTNET_INSTALL_DIR={{ retronas_root }}/bin/dotnetcore8\"\nEnvironment=\"PATH={{ retronas_root }}/bin/dotnetcore8:{{ retronas_root }}/bin/dotnetcore8/tools:$PATH\"\nExecStart={{ retronas_root }}/bin/webone/webone --daemon\nReadWriteDirectories=-/var/log/\nTimeoutStopSec=10\nRestart=on-failure\n\n[Install]\nWantedBy=default.target\n"
  },
  {
    "path": "ansible/templates/install_wrp/install_wrp.sh.j2",
    "content": "#!/bin/bash\n\nARCH=$(dpkg --print-architecture | head -n1)\n\n#    https://github.com/tenox7/wrp/releases/download/4.6.0/wrp-amd64-linux\n#    https://github.com/tenox7/wrp/releases/download/4.6.0/wrp-arm64-linux\n\n\nRELEASE=$( curl -kLs https://api.github.com/repos/tenox7/wrp/releases | jq -r \".[0].assets | map(select(.name | match (\\\"wrp-${ARCH}-linux\\\")))[-1] | .browser_download_url\" )\n\n[ ! -d /opt/wrp ] && mkdir -p /opt/wrp\ncd /opt/wrp\n\ncurl -kLsO \"${RELEASE}\"\n\nchmod +x $(basename ${RELEASE})\n"
  },
  {
    "path": "ansible/templates/install_wrp/wrp.service.j2",
    "content": "[Unit]\nDescription={{ my_name|upper }} service\nAfter=network.target\n\n[Service]\nType=simple\nRestart=always\nExecStart=/opt/{{ my_name }}/{{ my_name }}-{{ architecture.stdout }}-linux -h -l :64888\nSyslogIdentifier={{ my_name|upper }}\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "ansible/templates/install_x11vnc/x11vnc_wrapper.sh.j2",
    "content": "#!/bin/bash\n\nXDISPLAY=$1\nXAUTH=$2\nPORT=$3\n\nRFBAUTH=/etc/vncpasswd_retronas\n\n# use a local auth file if it exists\nif [ -f /home/$USER/vncpasswd_retronas ]\nthen\n    RFBAUTH=/home/$USER/vncpasswd_retronas\nfi\n\nx11vnc -quiet -display $XDISPLAY -auth $XAUTH -listen 0.0.0.0 -rfbport $PORT -rfbauth $RFBAUTH -forever\n"
  },
  {
    "path": "ansible/templates/install_xbox/retronas_xbox.conf.j2",
    "content": "## RetroNAS config.\n## This is autogenerated. Changes will be lost.\ncomment = xbox\npath = {{ retronas_path }}/xbox\nguest ok = no\nbrowseable = yes\nwrite list = {{ retronas_user }}\nwriteable = yes\nvalid users = {{ retronas_user }}\ncreate mask = 0775\ndirectory mask = 0775\nfollow symlinks = yes\nwide links = yes\n"
  },
  {
    "path": "ansible/templates/install_xbox360/retronas_xbox360.conf.j2",
    "content": "## RetroNAS config.\n## This is autogenerated. Changes will be lost.\ncomment = xbox360\npath = {{ retronas_path }}/xbox360/games\nguest ok = no\nbrowseable = yes\nvalid users = {{ retronas_user }}\ncreate mask = 0775\ndirectory mask = 0775\nwriteable = yes"
  },
  {
    "path": "ansible/templates/install_xbox360_netiso/dummy.iso.j2",
    "content": ""
  },
  {
    "path": "ansible/templates/install_xbox360_netiso/install_xbox360_netiso.sh.j2",
    "content": "#!/bin/bash\n\nset -ue\n\nARCH=$(uname -m)\nGH_OWNER=tuxuser\nGH_REPO=netiso-srv\nDEST_PATH=/opt/xbox360_netiso\n\nRELEASE=$( curl -kLs https://api.github.com/repos/${GH_OWNER}/${GH_REPO}/releases | jq -r \".[0].assets | map(select(.name | match (\\\"netiso-srv-${ARCH}-unknown-linux-musl\\\")))[-1] | .browser_download_url\" )\n\n[ ! -d \"${DEST_PATH}\" ] && mkdir -p \"${DEST_PATH}\"\ncd \"${DEST_PATH}\"\n\ncurl -kLsO \"${RELEASE}\"\n\nRELEASE_FILE=$( basename ${RELEASE} )\nif [ ! -z $RELEASE ]\nthen\n  if [ -f \"${RELEASE_FILE}\" ]\n  then\n    unzip \"$( basename ${RELEASE_FILE})\" -d \"_temp\"\n    TARGET=$(find -type f -name \"netiso-srv\")\n    mv $TARGET ${DEST_PATH}\n    chmod +x ${DEST_PATH}/$( basename ${TARGET})\n  else\n    echo \"Failed\"\n    exit 1\n  fi\nelse\n  echo \"Download failed\"\n  exit 1\nfi\n\nrm -f ${RELEASE_FILE}\nrm -rf ${DEST_PATH}/_temp\n"
  },
  {
    "path": "ansible/templates/install_xbox360_netiso/xbox360_netiso.service.j2",
    "content": "[Unit]\nDescription={{ my_name|upper }} service\nAfter=network.target\n\n[Service]\nUser={{ retronas_user }}\nType=simple\nRestart=always\nExecStart=/opt/{{ module_name }}/netiso-srv -r -v {{ retronas_path }}/{{ system_key }}\nSyslogIdentifier={{ my_name|upper }}\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "ansible/templates/install_xboxmanager/xboxmanager.cfg.j2",
    "content": "{\n\"xbox_username\":\"xbox\",\n\"xbox_password\":\"xbox\",\n\"xbox_ipaddress\":\"192.168.1.100\",\n\"xbox_type\":\"games\",\n\"xbox_games_drive\":\"E\",\n\"xbox_games_directory\":\"games\",\n\"local_games_directory\":\"{{ retronas_path }}/microsoft/xbox/iso\",\n\"local_mount_directory\":\"{{ retronas_path }}/device-mounts/xbox\"\n}"
  },
  {
    "path": "ansible/templates/install_xlink-kai/xlink-kai.service.j2",
    "content": "[Unit]\nDescription=xlink-kai\nAfter=network.target\nStartLimitIntervalSec=60\nStartLimitBurst=4\n\n[Service]\nUser={{ retronas_user }}\nExecStart=/usr/bin/kaiengine\nKillSignal=3\nRestartKillSignal=3\nSuccessExitStatus=3 SIGQUIT QUIT\nRestart=always\nRestartSec=5\n\n[Install]\nWantedBy=multi-user.target"
  },
  {
    "path": "ansible/templates/install_ytree/install_ytree.sh.j2",
    "content": "#!/bin/bash\n\nset -u\n\nSRCDIR=\"{{ retronas_root }}/src\"\nBINDIR=\"/usr/local/bin\"\nREPO=https://github.com/lattenero/ytree\n\n\necho \"Configuring build directories...\"\n[ -d ${SRCDIR} ] && rm -rf \"${SRCDIR}\"\nmkdir -p \"${SRCDIR}\"\ncd \"${SRCDIR}\"\n\necho \"Downloading git source ...\"\ngit clone $REPO\ncd \"${SRCDIR}/$(basename $REPO .git)\"\n\necho \"Building\"\nmake -j5\n\necho \"Moving binary to RetroNAS bin dir...\"\nmkdir -p \"${BINDIR}\" 2>/dev/null\nmv -vf ytree \"${BINDIR}/\"\n\necho \"Cleaning up...\"\n[ -d ${SRCDIR} ] && rm -rf \"${SRCDIR}\"\n\necho \"All done!\""
  },
  {
    "path": "ansible/templates/install_zterm/install_zterm.sh.j2",
    "content": "#!/bin/bash\n\nset -u\n\nOUTDIR=/opt/zterm\nATMPDIR=/tmp/zterm\nREPO=https://github.com/sairuk/zterm\n\n# clean up old files\n[ -d \"$ATMPDIR\" ] && rm -rf \"$ATMPDIR\"\n\n# clone\ncd /tmp\ngit clone $REPO\n\n# build\ncd \"$ATMPDIR/build\"\ncmake ..\nmake\n\n# dir\n[ ! -d $OUTDIR ] && mkdir -p $OUTDIR\n\n# move\nif [ -f $ATMPDIR/build/zterm ]\nthen\n    mv $ATMPDIR/build/z* $OUTDIR\n\n    #fix path\n    sed -i -r 's#^DIR.+#DIR={{ retronas_path }}#' $OUTDIR/zconfig\nfi"
  },
  {
    "path": "ansible/templates/install_zterm/zterm.service.j2",
    "content": "[Unit]\nDescription=zterm zmodem file transfer server\nStartLimitIntervalSec=60\nStartLimitBurst=4\nConditionPathExists=/dev/ttyUSB0\n\n[Service]\nUser={{ retronas_user }}\nWorkingDirectory=/opt/zterm/\nExecStart=/opt/zterm/zterm\nRestart=on-failure\nRestartSec=2\nSuccessExitStatus=3 4\nRestartForceExitStatus=3 4\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "config/menu/3ds_qr.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"3DS QR Codes\",\n        \"description\": \"Nintendo 3DS FBI homebrew\",\n        \"id\":\"3DS QR Codes\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install Generator\",\n                \"id\":\"3ds_qr_codes\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"3ds_qr_codes\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Generate\",\n                \"description\": \"Generate QR Codes\",\n                \"id\":\"3ds_qr\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"\",\n                \"command\": \"3ds_qr\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Regenerate\",\n                \"description\": \"Regenerate all QR codes\",\n                \"id\":\"3ds_qr_flush\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"\",\n                \"command\": \"3ds_qr\",\n                \"args\": \"-f\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"Interface\",\n                \"description\": \"Select target interface to listen on\",\n                \"id\":\"retronas_net_3dsqr_interface\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"set-retronas-net-3dsqr-interface\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"06\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"3DS-QR-codes.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/_template.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"\",\n        \"description\": \"\",\n        \"id\":\"\",\n        \"prompt\": \"\",\n        \"type\": \"\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/adtpro.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"adtpro\",\n        \"description\": \"ADTPro transfers data \\\\(physical disks and/or disk images\\\\) between Apple II-era computers and RetroNAS||We launch this service in headless localhost mode \\\\(serial over IP\\\\), you can use VNC to access the gui with the full feature set||Note: Requires a VNC client connected to retronas:60000\",\n        \"id\":\"adtpro-menu\",\n        \"prompt\": \"\",\n        \"type\": \"modal\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install adtpro application\",\n                \"id\":\"adtpro\",\n                \"prompt\": \"Install\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"adtpro\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"03\",\n                \"title\": \"Edit\",\n                \"description\": \"Change the localhost (SIP) config\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"form\",\n                \"group\":\"\",\n                \"command\": \"adtpro_localhost_edit\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Start\",\n                \"description\": \"Start adtpro localhost mode\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"adtpro@localhost\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop adtpro localhost mode\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"adtpro@localhost\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"06\",\n                \"title\": \"Status\",\n                \"description\": \"Query adtpro localhost mode\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_status\",\n                \"group\":\"\",\n                \"command\": \"adtpro@localhost\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"07\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"ADTPro.md\",\n                \"args\": \"\"\n            }\n        ]\n    },\n    \"adtpro-serial\": \n    {\n        \"title\": \"adtpro\",\n        \"description\": \"Please select an option\",\n        \"id\":\"adtpro-serial-menu\",\n        \"prompt\": \"\",\n        \"type\": \"modal\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"02\",\n                \"title\": \"Edit\",\n                \"description\": \"Serial device settings\", \n                \"id\":\"adtpro-edit-serial\",\n                \"prompt\": \"\",\n                \"type\": \"form\",\n                \"group\":\"\",\n                \"command\": \"adtpro_serial_edit\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Start\",\n                \"description\": \"Start adtpro serial mode\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"adtpro@serial\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop adtpro serial mode\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"adtpro@serial\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"Status\",\n                \"description\": \"Query adtpro serial mode\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_status\",\n                \"group\":\"\",\n                \"command\": \"adtpro@serial\",\n                \"args\": \"\"\n            }\n        ]\n    },\n    \"adtpro-ethernet\": \n    {\n        \"title\": \"adtpro\",\n        \"description\": \"Please select an option\",\n        \"id\":\"adtpro-ethernet-menu\",\n        \"prompt\": \"\",\n        \"type\": \"modal\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"02\",\n                \"title\": \"Edit\",\n                \"description\": \"Change the Ethenet net listen port\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"form\",\n                \"group\":\"\",\n                \"command\": \"adtpro_ethernet_edit\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Start\",\n                \"description\": \"Start adtpro ethernet mode\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"adtpro@ethernet\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop adtpro ethernet mode\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"adtpro@ethernet\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"Status\",\n                \"description\": \"Query adtpro ethernet mode\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_status\",\n                \"group\":\"\",\n                \"command\": \"adtpro@ethernet\",\n                \"args\": \"\"\n            }\n        ]\n    },\n    \"adtpro-audio\": \n    {\n        \"title\": \"adtpro\",\n        \"description\": \"Please select an option\",\n        \"id\":\"adtpro-audio-menu\",\n        \"prompt\": \"\",\n        \"type\": \"modal\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Start\",\n                \"description\": \"Start adtpro audio mode\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"adtpro@audio\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop adtpro audio mode\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"adtpro@audio\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Status\",\n                \"description\": \"Query adtpro audio mode\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_status\",\n                \"group\":\"\",\n                \"command\": \"adtpro@audio\",\n                \"args\": \"\"\n            }\n        ]\n    },\n    \"adtpro-localhost\": \n    {\n        \"title\": \"adtpro\",\n        \"description\": \"Please select an option, note in this mode ADTPro is a client\",\n        \"id\":\"adtpro-localhost-menu\",\n        \"prompt\": \"\",\n        \"type\": \"modal\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"02\",\n                \"title\": \"Edit\",\n                \"description\": \"Change the localhost (SIP) config\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"form\",\n                \"group\":\"\",\n                \"command\": \"adtpro_localhost_edit\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Start\",\n                \"description\": \"Start adtpro localhost mode\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"adtpro@localhost\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop adtpro localhost mode\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"adtpro@localhost\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"Status\",\n                \"description\": \"Query adtpro localhost mode\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_status\",\n                \"group\":\"\",\n                \"command\": \"adtpro@localhost\",\n                \"args\": \"\"\n            }\n        ]\n    },\n    \"adtpro-serial-edit\": \n    {\n        \"title\": \"ADTPro Serial Config\",\n        \"description\": \"set ADTPro device\",\n        \"id\":\"adtpro-serial-edit-menu\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": []\n    },\n    \"adtpro-ethernet-edit\": \n    {\n        \"title\": \"ADTPro Ethernet Config\",\n        \"description\": \"set ADTPro device\",\n        \"id\":\"adtpro-ethernet-edit-menu\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": []\n    }\n}"
  },
  {
    "path": "config/menu/assembly64.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"assembly64\",\n        \"description\": \"assembly64 project, A tool that helps you bulk download artifacts to your c64.||Note: Requires a VNC client connected to retronas:60001\",\n        \"id\":\"assembly64-menu\",\n        \"prompt\": \"\",\n        \"type\": \"modal\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install assembly64 application\",\n                \"id\":\"assembly64\",\n                \"prompt\": \"Install\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"assembly64\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Start\",\n                \"description\": \"Start assembly64\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"assembly64\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop assembly64\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"assembly64\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"Status\",\n                \"description\": \"Query assembly64\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_status\",\n                \"group\":\"\",\n                \"command\": \"assembly64\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"06\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"assembly64.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config/menu/atarist-sidecart.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"Atari ST Sidecart\",\n        \"description\": \"Manage Sidecart support\",\n        \"id\":\"sidecart\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install sidecart support\",\n                \"id\":\"sidecart-install\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"sidecart\",\n                \"command\": \"atarist-sidecart\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Update DB\",\n                \"description\": \"Update the local floppy DB\",\n                \"id\":\"sidecart-db\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"sidecart\",\n                \"command\": \"atarist-sidecart-updatedb\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Update ROMS\",\n                \"description\": \"Update ROM listing\",\n                \"id\":\"sidecart-roms\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"sidecart\",\n                \"command\": \"atarist-sidecart-generate-roms\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"Mirror\",\n                \"description\": \"Build a local mirror\",\n                \"id\":\"sidecart-mirror\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"sidecart\",\n                \"command\": \"atarist-sidecart-mirrordb\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"06\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"Sidecart.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/cockpit.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"cockpit\",\n        \"description\": \"Install cockpit\",\n        \"id\":\"cockpit\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install cockpit\",\n                \"id\":\"cockpit\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"cockpit\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"cockpit\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"cockpit\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"cockpit\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"Cockpit.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/config.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"Configuration\",\n        \"description\": \"Please select a configuration||NOTE: changes to these settings may require reinstallation of tools||Current RetroNAS settings| User/Group: \\\"${OLDRNUSER}\\\":\\\"${OLDRNGROUP}\\\"| Directory: \\\"${OLDRNPATH}\\\"| Branch: \\\"${OLDRNBRANCH}\\\"| \\\"${IPADDMSG}\\\"\",\n        \"id\":\"config\",\n        \"prompt\": \"Change\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Username\",\n                \"description\": \"Configure RetroNAS user\",\n                \"id\":\"update-user\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"update-user\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Group\",\n                \"description\": \"Configure RetroNAS group\",\n                \"id\":\"update-group\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"update-group\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Password\",\n                \"description\": \"Configure RetroNAS password\",\n                \"id\":\"update-password\",\n                \"prompt\": \"\",\n                \"type\": \"modal\",\n                \"group\":\"\",\n                \"command\": \"retronas_password\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"Data Directory\",\n                \"description\": \"Configure RetroNAS top level directory\",\n                \"id\":\"set-top-level-dir\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"set-top-level-dir\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"06\",\n                \"title\": \"Permissions\",\n                \"description\": \"Fix ALL on-disk permissions\",\n                \"id\":\"permissions\",\n                \"prompt\": \"Run\",\n                \"type\": \"modal\",\n                \"group\":\"\",\n                \"command\": \"retronas_fixperms\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"08\",\n                \"title\": \"Profiles\",\n                \"description\": \"Install a preconfigured profile\",\n                \"id\":\"profiles\",\n                \"prompt\": \"\",\n                \"type\": \"modal\",\n                \"group\":\"\",\n                \"command\": \"profiles\",\n                \"args\": \"\"\n            }\n        ]\n    },\n    \"update-user\": \n    {\n        \"title\": \"Update User\",\n        \"description\": \"Please enter the username for all RetroNAS services to run as||This will normally default to \\\"pi\\\" on Raspberry Pi OS Install||It is recommended you leave it as default unless you know what you are doing.\",\n        \"id\":\"update-user-menu\",\n        \"prompt\": \"Update\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Username\",\n                \"description\": \"\",\n                \"id\":\"update-user\",\n                \"prompt\": \"\",\n                \"type\": \"input:input\",\n                \"group\":\"retronas\",\n                \"command\": \"update-username\",\n                \"args\": \"\"\n            }\n        ]\n    },\n    \"update-user-confirm\": \n    {\n        \"title\": \"Confirm User\",\n        \"description\": \"Do you want to save this setting?|New RetroNAS user: \\\"${NEWVALUE}\\\"\",\n        \"id\":\"update-user-confirm-menu\",\n        \"prompt\": \"Confirm\",\n        \"type\": \"dialog-yn\",\n        \"items\": []\n    },\n    \"update-group\": \n    {\n        \"title\": \"Update Group\",\n        \"description\": \"Please enter the group for all RetroNAS services to run as||This will normally default to \\\"pi\\\" on Raspberry Pi OS Install||It is recommended you leave it as default unless you know what you are doing.\",\n        \"id\":\"update-group-menu\",\n        \"prompt\": \"Update\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Group\",\n                \"description\": \"\",\n                \"id\":\"update-group\",\n                \"prompt\": \"\",\n                \"type\": \"input:input\",\n                \"group\":\"retronas\",\n                \"command\": \"update-group\",\n                \"args\": \"\"\n            }\n        ]\n    },\n    \"update-group-confirm\": \n    {\n        \"title\": \"Confirm Group\",\n        \"description\": \"Do you want to save this setting?|New RetroNAS group: \\\"${NEWVALUE}\\\"\",\n        \"id\":\"update-group-confirm-menu\",\n        \"prompt\": \"Confirm\",\n        \"type\": \"dialog-yn\",\n        \"items\": []\n    },\n    \"update-password\": \n    {\n        \"title\": \"Update Password\",\n        \"description\": \"If you are having problems with CIFS/SMB/Appletalk shares, you can reset their password here, this will also update your user password||Passwords entered here will be echoed to the backend processes||Use the up/down arrows to navigate form fields\",\n        \"id\":\"update-password-menu\",\n        \"prompt\": \"Update\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Update Password\",\n                \"description\": \"Update the user Systems/Samba password\",\n                \"id\":\"update-password\",\n                \"prompt\": \"\",\n                \"type\": \"input:password\",\n                \"group\":\"retronas\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            }\n        ]\n    },\n    \"set-top-level-dir\": \n    {\n        \"title\": \"Set Top Level Directory\",\n        \"description\": \"Please type in the RetroNAS top level directory\",\n        \"id\":\"set-top-level-dir-menu\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Directory Name\",\n                \"description\": \"Set top Level Dir\",\n                \"id\":\"set-top-level-dir\",\n                \"prompt\": \"\",\n                \"type\": \"input:input\",\n                \"group\":\"retronas\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            }\n        ]\n    },\n    \"set-top-level-dir-confirm\": \n    {\n        \"title\": \"Confirm EtherDFS NIC\",\n        \"description\": \"Do you want to save this setting?|New RetroNAS top level directory: \\\"${NEWVALUE}\\\"\",\n        \"id\":\"set-top-level-dir-confirm\",\n        \"prompt\": \"Confirm\",\n        \"type\": \"dialog-yn\",\n        \"items\": []\n    },\n    \"set-etherdfs-nic\": \n    {\n        \"title\": \"Set EtherDFS NIC\",\n        \"description\": \"Please enter the interface name for EtherDFS to bind to.||Normally this is something like eth0 for wired Ethernet.||If EtherDFS is installed, you will need to re-run the installer to apply the change.\",\n        \"id\":\"set-etherdfs-nic-menu\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Set Device\",\n                \"description\": \"Set EtherDFS device\",\n                \"id\":\"set-etherdfs-nic\",\n                \"prompt\": \"\",\n                \"type\": \"input:input\",\n                \"group\":\"etherdfs\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            }\n        ]\n    },\n    \"set-etherdfs-nic-confirm\": \n    {\n        \"title\": \"Confirm EtherDFS NIC\",\n        \"description\": \"Do you want to save this setting?|New EtherDFS interface: \\\"${NEWVALUE}\\\"\",\n        \"id\":\"set-etherdfs-nic-confirm\",\n        \"prompt\": \"Confirm\",\n        \"type\": \"dialog-yn\",\n        \"items\": []\n    }\n}"
  },
  {
    "path": "config/menu/deluge.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"deluge\",\n        \"description\": \"bitorrent server/client\",\n        \"id\":\"deluge-menu\",\n        \"prompt\": \"\",\n        \"type\": \"modal\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install deluge application\",\n                \"id\":\"deluge\",\n                \"prompt\": \"Install\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"deluge\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Start\",\n                \"description\": \"Start daemon\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"deluge\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop daemon\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"deluge\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"Status\",\n                \"description\": \"Query daemon\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_status\",\n                \"group\":\"\",\n                \"command\": \"deluge*\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"06\",\n                \"title\": \"Upgrade\",\n                \"description\": \"Upgrade to packages from debian testing\",\n                \"id\":\"deluge\",\n                \"prompt\": \"Upgrade\",\n                \"type\": \"script\",\n                \"group\":\"\",\n                \"command\": \"upgrade_deluge\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"07\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"BitTorrent.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/dexdrive.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"dexdrive\",\n        \"description\": \"linux-dexdrive project read PSX/N64 memory cards and linux block devices\",\n        \"id\":\"dexdrive-menu\",\n        \"prompt\": \"\",\n        \"type\": \"modal\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install linux-dexdrive application\",\n                \"id\":\"linux-dexdrive\",\n                \"prompt\": \"Install\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"linux-dexdrive\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"03\",\n                \"title\": \"Edit\",\n                \"description\": \"Change the serial device\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"form\",\n                \"group\":\"\",\n                \"command\": \"dexdrive_serial_edit\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Start\",\n                \"description\": \"Start Dex Drive daemon\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"linux-dexdrive\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop Dex Drive daemon\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"linux-dexdrive\",\n                \"args\": \"\"  \n            },\n            {\n                \"index\":\"06\",\n                \"title\": \"Status\",\n                \"description\": \"Query Dex Drive daemon\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_status\",\n                \"group\":\"\",\n                \"command\": \"linux-dexdrive\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"07\",\n                \"title\": \"Dump\",\n                \"description\": \"Dump a PS1 memory card\",\n                \"id\":\"dexdrive_dumper\",\n                \"prompt\": \"Dump\",\n                \"type\": \"script\",\n                \"group\":\"\",\n                \"command\": \"dexdrive_dumper\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"08\",\n                \"title\": \"Image\",\n                \"description\": \"Write an image to a memory card\",\n                \"id\":\"dexdrive_memcards\",\n                \"prompt\": \"Dump\",\n                \"type\": \"dialog\",\n                \"group\":\"\",\n                \"command\": \"dexdrive_memcards\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"linux-dexdrive.md\",\n                \"args\": \"\"\n            }\n        ]\n    },\n    \"dexdrive-serial-edit\": \n    {\n        \"title\": \"Dex Drive Serial Config\",\n        \"description\": \"set Dex Drive device\",\n        \"id\":\"dexdrive-serial-edit-menu\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": []\n    },\n    \"dexdrive-memcards\": \n    {\n        \"title\": \"Dex Drive Image Chooser\",\n        \"description\": \"Please choose an image to write to the memory card\",\n        \"id\":\"dexdrive-memcards\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config/menu/doc.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"Documentation\",\n        \"description\": \"Project documentation||Documentation will open in the Lynx browser \\\\(press Q to quit browser\\\\), documentation is an optional installation\",\n        \"id\":\"documentation\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"02\",\n                \"title\": \"README\",\n                \"description\": \"Readme file\",\n                \"id\": \"readme\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\": \"\",\n                \"command\": \"README\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"03\",\n                \"title\": \"SECURIY\",\n                \"description\": \"SECURITY warning\",\n                \"id\": \"security\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\": \"\",\n                \"command\": \"SECURITY-WARNING.md\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"04\",\n                \"title\": \"Generic Roms Folder\",\n                \"description\": \"Information on RetroNAS generic file structure\",\n                \"id\": \"generic-roms-folder\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\": \"\",\n                \"command\": \"Generic-ROMs-folder.md\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"05\",\n                \"title\": \"Supported Clients\",\n                \"description\": \"Information on RetroNAS supported clients\",\n                \"id\": \"supported-clients\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\": \"\",\n                \"command\": \"Supported-clients.md\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"06\",\n                \"title\": \"Supported Configurations\",\n                \"description\": \"Information on RetroNAS supported configurations\",\n                \"id\": \"supported-configurations\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\": \"\",\n                \"command\": \"Supported-Configurations.md\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"07\",\n                \"title\": \"Testing\",\n                \"description\": \"Information on RetroNAS supported configurations\",\n                \"id\": \"testing-retronas\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\": \"\",\n                \"command\": \"Testing-RetroNAS.md\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"08\",\n                \"title\": \"Structure\",\n                \"description\": \"RetroNAS structure\",\n                \"id\": \"structure-retronas\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\": \"\",\n                \"command\": \"Structure.md\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"09\",\n                \"title\": \"Bugs\",\n                \"description\": \"Troubleshooting/Reporting bugs\",\n                \"id\": \"bug\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\": \"\",\n                \"command\": \"Bugs.md\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"98\",\n                \"title\": \"Ideas\",\n                \"description\": \"A list of features/projects that would be suited for inclusion in RetroNAS\",\n                \"id\": \"ideas\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\": \"\",\n                \"command\": \"Ideas.md\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"99\",\n                \"title\": \"Notices\",\n                \"description\": \"Major project change notices\",\n                \"id\": \"notices\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\": \"\",\n                \"command\": \"Notices.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/dreampi.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"dreampi\",\n        \"description\": \"Install dreampi\",\n        \"id\":\"dreampi\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install dreampi (Kazade version)\",\n                \"id\":\"dreampi\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"dreampi\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"dreampi\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"dreampi\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"dreampi\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"DreamPi.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config/menu/etherdfs.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"etherdfs\",\n        \"description\": \"Install etherdfs\",\n        \"id\":\"etherdfs\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install etherdfs\",\n                \"id\":\"etherdfs\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"etherdfs\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"EtherDFS\",\n                \"description\": \"Set EtherDFS/ethflopd network interface\",\n                \"id\":\"set-etherdfs-nic\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"set-etherdfs-nic\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"etherdfs\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"etherdfs\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"etherdfs\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"EtherDFS.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/ethflopd.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"ethflopd\",\n        \"description\": \"Install ethflopd\",\n        \"id\":\"ethflopd\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install ethflopd\",\n                \"id\":\"ethflopd\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"ethflopd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"ethflopd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"ethflopd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"ethflopd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"ethflop.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/experimental.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"EXPERIMENTAL\",\n        \"description\": \"WARNING these tools are EXPERIMENTAL, may break your system or destroy your data!||Do not use these unless you are comfortable with losing everything\",\n        \"id\":\"experimental\",\n        \"prompt\": \"Install\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"02\",\n                \"title\": \"webui\",\n                \"description\": \"Experimental retronas webui (cockpit)\", \n                \"id\":\"cockpit-retronas\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"cockpit-retronas\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"03\",\n                \"title\": \"network\",\n                \"description\": \"Network presets (highly experimental!)\",\n                \"id\":\"network\",\n                \"prompt\": \"\",\n                \"type\": \"dialog\",\n                \"group\":\"\",\n                \"command\": \"network\"\n            },\n            {\n              \"index\": \"04\",\n              \"title\": \"xbox\",\n              \"description\": \"Microsoft XBOX SMB config, for use w/Project Stellar\",\n              \"id\": \"xbox\",\n              \"prompt\": \"\",\n              \"type\": \"install\",\n              \"group\": \"samba\",\n              \"command\": \"xbox\",\n              \"args\": \"\"\n            },\n            {\n              \"index\": \"05\",\n              \"title\": \"flippydrive\",\n              \"description\": \"Flippydrive Network service\",\n              \"id\": \"flippydrive\",\n              \"prompt\": \"\",\n              \"type\": \"dialog\",\n              \"group\": \"\",\n              \"command\": \"flippydrive\",\n              \"args\": \"\"\n            },\n            {\n              \"index\": \"06\",\n              \"title\": \"x360 netiso\",\n              \"description\": \"Xbox360 NetISO service\",\n              \"id\": \"xbox360_netiso\",\n              \"prompt\": \"\",\n              \"type\": \"dialog\",\n              \"group\": \"\",\n              \"command\": \"xbox360_netiso\",\n              \"args\": \"\"\n            },\n            {\n              \"index\": \"07\",\n              \"title\": \"netmount\",\n              \"description\": \"DOS NetMount service\",\n              \"id\": \"netmount\",\n              \"prompt\": \"\",\n              \"type\": \"dialog\",\n              \"group\": \"\",\n              \"command\": \"netmount\",\n              \"args\": \"\"\n            },\n            {\n              \"index\": \"08\",\n              \"title\": \"mister-organize\",\n              \"description\": \"MiSTer Organize\",\n              \"id\": \"mister-organize\",\n              \"prompt\": \"\",\n              \"type\": \"dialog\",\n              \"group\": \"\",\n              \"command\": \"mister-organize\",\n              \"args\": \"\"\n            },\n            {\n              \"index\": \"09\",\n              \"title\": \"sit\",\n              \"description\": \"Create StuffIt archives on Unix systems\",\n              \"id\": \"sit\",\n              \"prompt\": \"\",\n              \"type\": \"install\",\n              \"group\": \"\",\n              \"command\": \"sit\",\n              \"args\": \"\"\n            },\n            {\n                \"index\": \"10\",\n                \"title\": \"hfsutils\",\n                \"description\": \"Install patched version of hfsutils\", \n                \"id\":\"hfsutils\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"hfsutils\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config/menu/fenrirodewebserver.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"Fenrir-ODE Web Server\",\n        \"description\": \"\",\n        \"id\":\"fenrir-ode-menu\",\n        \"prompt\": \"\",\n        \"type\": \"modal\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install the Fenrir-ODE Webserver application\",\n                \"id\":\"fenrir-ode-webserver\",\n                \"prompt\": \"Install\",\n                \"type\":\"install\",\n                \"group\":\"\",\n                \"command\": \"fenrir-ode-webserver\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Gamelist\",\n                \"description\": \"Manually refresh the gamelist\",\n                \"id\":\"\",\n                \"prompt\": \"Refresh\",\n                \"type\": \"service_restart\",\n                \"group\":\"\",\n                \"command\": \"fenrir-ode-webserver\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"fenrir-ode-webserver\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"fenrir-ode-webserver\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"fenrir-ode-webserver\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"Fenrir-ODE-Webserver.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/flippydrive.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"flippydrive\",\n        \"description\": \"Install flippydrive\",\n        \"id\":\"flippydrive\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install flippydrive\",\n                \"id\":\"flippydrive\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"flippydrive\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"flippydrive\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"flippydrive\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"flippydrive\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"Flippydrive.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config/menu/fsp.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"fsp\",\n        \"description\": \"Install fsp\",\n        \"id\":\"fsp\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install fsp\",\n                \"id\":\"fsp\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"fsp\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"fspd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"fspd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"fspd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"FSP.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/gogrepo.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"GOG\",\n        \"description\": \"Current OS: \\\"${OLDGOGOS}\\\"||Current LANG: \\\"${OLDGOGLANG}\\\"||WARNING: There is currently known issues with this tool,|see https://github.com/eddie3/gogrepo/issues/63||Please choose a task\",\n        \"id\":\"gogrepo\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"02\",\n                \"title\": \"Install\",\n                \"description\": \"Download your GOG game installers\",\n                \"id\": \"gogrepo\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\": \"\",\n                \"command\": \"gogrepo\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Authentication\",\n                \"description\": \"Configure GOG authentication\",\n                \"id\":\"gogrepo-auth\",\n                \"prompt\": \"\",\n                \"type\": \"dialog\",\n                \"group\":\"gogrepo\",\n                \"command\": \"gogrepo\",\n                \"args\": \"auth-chooser\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Select OS\",\n                \"description\": \"Change OS preferences\",\n                \"id\":\"gog-os-input\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"gogrepo\",\n                \"command\": \"gogrepo_wrapper\",\n                \"args\": \"select-os\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"Select Lang\",\n                \"description\": \"Change Language preferences\",\n                \"id\":\"gog-lang-input\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"gogrepo\",\n                \"command\": \"gogrepo_wrapper\",\n                \"args\": \"select-lang\"\n            },\n            {\n                \"index\":\"06\",\n                \"title\": \"Sync\",\n                \"description\": \"Syncronise my games list\",\n                \"id\":\"gog-sync-gameslist\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"gogrepo\",\n                \"command\": \"gogrepo-wrapper\",\n                \"args\": \"update-skip-known\"\n            },\n            {\n                \"index\":\"07\",\n                \"title\": \"Single\",\n                \"description\": \"Download/update a single game\",\n                \"id\":\"gog-dl-one-game\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"gogrepo\",\n                \"command\": \"gogrepo-wrapper\",\n                \"args\": \"update-download-single\"\n            },\n            {\n                \"index\":\"08\",\n                \"title\": \"All\",\n                \"description\": \"Download/update all games\",\n                \"id\":\"gog-dl-all-game\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"gogrepo\",\n                \"command\": \"gogrepo-wrapper\",\n                \"args\": \"download-all\"\n            },\n            {\n                \"index\":\"09\",\n                \"title\": \"FULL\",\n                \"description\": \"Synchronise games list & download/update all games\",\n                \"id\":\"gog-dl-full-game\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"gogrepo\",\n                \"command\": \"gogrepo-wrapper\",\n                \"args\": \"update-download\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"gogrepo.md\",\n                \"args\": \"\"\n            }\n        ]\n    },\n    \"gogrepo-setupos\": \n    {\n        \"title\": \"GOG configure OS\",\n        \"description\": \"Current Operating Systems: ${OLDGOGOS}||Please set the Operating System\\\\(s\\\\) you would like to download GOG games for.\",\n        \"id\":\"gogrepo-setupos\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Windows\",\n                \"description\": \"Only Windows Games\",\n                \"id\":\"gogos-windows\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Mac\",\n                \"description\": \"Only Mac Games\",\n                \"id\":\"gogos-mac\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Linux\",\n                \"description\": \"Only Linux Games\",\n                \"id\":\"gogos-linux\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"Windows & Mac\",\n                \"description\": \"Only Windows & Mac Games\",\n                \"id\":\"gogos-windows-mac\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"06\",\n                \"title\": \"Windows & Linux\",\n                \"description\": \"Only Windows & Linux Games\",\n                \"id\":\"gogos-windows-linux\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"07\",\n                \"title\": \"Mac & Linux\",\n                \"description\": \"Only Mac & Linux Games\",\n                \"id\":\"gogos-mac-linux\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"08\",\n                \"title\": \"All\",\n                \"description\": \"All Games\",\n                \"id\":\"gogos-all\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            }\n        ]\n    },\n    \"gogrepo-setuplang\": \n    {\n        \"title\": \"GOG configure Language\",\n        \"description\": \"Current Language: ${OLDGOGLANG}||Please set the Language you would like to download GOG games for.\",\n        \"id\":\"gogrepo-setuplang\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"en\",\n                \"title\": \"English\",\n                \"description\": \"\",\n                \"id\":\"gogos-en\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"ar\",\n                \"title\": \"Arabic\",\n                \"description\": \"\",\n                \"id\":\"gogos-en\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"bl\",\n                \"title\": \"Bulgarian\",\n                \"description\": \"\",\n                \"id\":\"gogos-bl\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"br\",\n                \"title\": \"Brazilian Portuguese\",\n                \"description\": \"\",\n                \"id\":\"gogos-br\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"cn\",\n                \"title\": \"Chinese\",\n                \"description\": \"\",\n                \"id\":\"gogos-cn\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"cz\",\n                \"title\": \"Czech\",\n                \"description\": \"\",\n                \"id\":\"gogos-cz\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"da\",\n                \"title\": \"Danish\",\n                \"description\": \"\",\n                \"id\":\"gogos-da\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"de\",\n                \"title\": \"German\",\n                \"description\": \"\",\n                \"id\":\"gogos-de\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"es\",\n                \"title\": \"Spanish\",\n                \"description\": \"\",\n                \"id\":\"gogos-es\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"fi\",\n                \"title\": \"Finnish\",\n                \"description\": \"\",\n                \"id\":\"gogos-fi\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"fr\",\n                \"title\": \"French\",\n                \"description\": \"\",\n                \"id\":\"gogos-fr\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"gk\",\n                \"title\": \"Greek\",\n                \"description\": \"\",\n                \"id\":\"gogos-gk\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"hu\",\n                \"title\": \"Hungarian\",\n                \"description\": \"\",\n                \"id\":\"gogos-hu\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"it\",\n                \"title\": \"Italian\",\n                \"description\": \"\",\n                \"id\":\"gogos-it\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"jp\",\n                \"title\": \"Japanese\",\n                \"description\": \"\",\n                \"id\":\"gogos-jp\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"ko\",\n                \"title\": \"Korean\",\n                \"description\": \"\",\n                \"id\":\"gogos-ko\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"nl\",\n                \"title\": \"Dutch\",\n                \"description\": \"\",\n                \"id\":\"gogos-nl\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"no\",\n                \"title\": \"Norse\",\n                \"description\": \"\",\n                \"id\":\"gogos-no\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"pl\",\n                \"title\": \"Polish\",\n                \"description\": \"\",\n                \"id\":\"gogos-pl\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"pt\",\n                \"title\": \"Portuguese\",\n                \"description\": \"\",\n                \"id\":\"gogos-pt\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"ro\",\n                \"title\": \"Romanian\",\n                \"description\": \"\",\n                \"id\":\"gogos-ro\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"                \n            },\n            {\n                \"index\": \"ru\",\n                \"title\": \"Russian\",\n                \"description\": \"\",\n                \"id\":\"gogos-ru\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"                \n            },\n            {\n                \"index\": \"sb\",\n                \"title\": \"Serbian\",\n                \"description\": \"\",\n                \"id\":\"gogos-sb\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"                \n            },\n            {\n                \"index\": \"sk\",\n                \"title\": \"Slovak\",\n                \"description\": \"\",\n                \"id\":\"gogos-sk\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"                \n            },\n            {\n                \"index\": \"sv\",\n                \"title\": \"Swedish\",\n                \"description\": \"\",\n                \"id\":\"gogos-sv\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"                \n            },\n            {\n                \"index\": \"tr\",\n                \"title\": \"Turkish\",\n                \"description\": \"\",\n                \"id\":\"gogos-tr\",\n                \"prompt\": \"\",\n                \"type\": \"\",\n                \"group\":\"gogrepo\",\n                \"command\": \"\",\n                \"args\": \"\"\n            }\n        ]\n    },\n    \"gogrepo-gamechooser\": \n    {\n        \"title\": \"GOG game chooser\",\n        \"description\": \"Please choose a game||If your game has not appeared, please synchronise your games list\",\n        \"id\":\"gogrepo-gamechooser\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            }\n        ]\n    },\n    \"gogrepo-auth\": \n    {\n        \"title\": \"GOG Authentication options\",\n        \"description\": \"Please choose a option for authtenticaiton||Login can break if GOG change something, cookie import is more reliable\",\n        \"id\":\"gogrepo-auth-chooser\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Login\",\n                \"description\": \"Configure GOG credentials\",\n                \"id\":\"gog-password\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"gogrepo\",\n                \"command\": \"gogrepo-wrapper\",\n                \"args\": \"login\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Import\",\n                \"description\": \"Import a cookies file, more reliable than login\",\n                \"id\":\"gog-cookies\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"gogrepo\",\n                \"command\": \"gogrepo-wrapper\",\n                \"args\": \"import-cookies\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/hbstorecdn.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"PS4 Homebrew Store CDN\",\n        \"description\": \"\",\n        \"id\":\"hb-store-cdn-menu\",\n        \"prompt\": \"\",\n        \"type\": \"modal\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install the PS4 Homebrew Store CDN\",\n                \"id\":\"hb-store-cdn\",\n                \"prompt\": \"Install\",\n                \"type\":\"install\",\n                \"group\":\"\",\n                \"command\": \"hb-store-cdn\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Package List\",\n                \"description\": \"Manually refresh the package list\",\n                \"id\":\"\",\n                \"prompt\": \"Refresh\",\n                \"type\": \"service_restart\",\n                \"group\":\"\",\n                \"command\": \"hb-store-cdn\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"hb-store-cdn\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"hb-store-cdn\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"hb-store-cdn\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"hb-store-cdn.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/hdparm.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"hdparm\",\n        \"description\": \"WARNING: These changes are irreversable, USE AT YOUR OWN RISK||Try in ORDER:|APM -\\\\> Standby -\\\\> Custom service \\\\!\\\\!LAST RESORT\\\\!\\\\! \\\\<-||The custom service reads a random sector from the drive at random intervals every 3-5m, this should be enough to keep the drive up and reduce any impact on the drive.\",\n        \"id\":\"hdparm-menu\",\n        \"prompt\": \"\",\n        \"type\": \"modal\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install the hdparm application\",\n                \"id\":\"hdparm\",\n                \"prompt\": \"Install\",\n                \"type\":\"install\",\n                \"group\":\"\",\n                \"command\": \"hdparm\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"10\",\n                \"title\": \"APM\",\n                \"description\": \"Disable Advanced Power Management (APM)\",\n                \"id\":\"hdparm-disable-apm\",\n                \"prompt\": \"Disable\",\n                \"type\": \"script\",\n                \"group\":\"\",\n                \"command\": \"hdparm-manager-disable-apm\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"11\",\n                \"title\": \"Standby\",\n                \"description\": \"Disable drive Standy\",\n                \"id\":\"hdparm-disable-standby\",\n                \"prompt\": \"Disable\",\n                \"type\": \"script\",\n                \"group\":\"\",\n                \"command\": \"hdparm-manager-disable-standby\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start hdparm service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"\",\n                \"command\": \"hdparm-manager-start-service\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query hdparm service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"script\",\n                \"group\":\"\",\n                \"command\": \"hdparm-manager-query-service\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the hdparm service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"\",\n                \"command\": \"hdparm-manager-stop-service\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"NEEDED.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/install.json",
    "content": "{\n  \"menu\": {\n    \"title\": \"Install\",\n    \"description\": \"Please select a module installer\",\n    \"id\": \"install\",\n    \"prompt\": \"Install\",\n    \"type\": \"menu\",\n    \"items\": [\n      {\n        \"index\": \"01\",\n        \"title\": \"Back\",\n        \"description\": \"Return to previous menu\",\n        \"id\": \"\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"group\": \"\",\n        \"command\": \"EXIT_OK\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"02\",\n        \"title\": \"3ds qr codes\",\n        \"description\": \"Nintendo 3DS FBI Homebrew installer\",\n        \"id\": \"3ds_qr\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"3ds_qr\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"03\",\n        \"title\": \"adtpro\",\n        \"description\": \"Apple Disk Transfer ProDOS\",\n        \"id\": \"adtpro\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"adtpro\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"04\",\n        \"title\": \"affstools\",\n        \"description\": \"Amiga Fast Filesystem tools\",\n        \"id\": \"affstools\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"affstools\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"05\",\n        \"title\": \"amiga samba\",\n        \"description\": \"Samba share for SMBMounter\",\n        \"id\": \"smbmounter\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"smbmounter\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"06\",\n        \"title\": \"amitools\",\n        \"description\": \"Various tools for using AmigaOS programs on other platforms\",\n        \"id\": \"amitools\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"amitools\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"07\",\n        \"title\": \"analoguepocket-cifs\",\n        \"description\": \"Samba for Analogue Pocket\",\n        \"id\": \"analoguepocket-cifs\",\n        \"prompt\": \"Install\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"analoguepocket_cifs\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"08\",\n        \"title\": \"apfs-fuse\",\n        \"description\": \"Apple File System fuse library\",\n        \"id\": \"apfs-fuse\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"apfs-fuse\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"09\",\n        \"title\": \"aria2\",\n        \"description\": \"lightweight multi-protocol/source cli download utility\",\n        \"id\": \"aria2\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"aria2\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"10\",\n        \"title\": \"assembly64\",\n        \"description\": \"assembly64 project (requires account)\",\n        \"id\": \"assembly64\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"assembly64\"\n      },\n      {\n        \"index\": \"11\",\n        \"title\": \"batocera\",\n        \"description\": \"Batocera CIFS config\",\n        \"id\": \"batocera-cifs\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"batocera_cifs\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"12\",\n        \"title\": \"cockpit\",\n        \"description\": \"Web based Linux system manager\",\n        \"id\": \"cockpit\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"cockpit\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"13\",\n        \"title\": \"cue2pops\",\n        \"description\": \"Convert PS1 images for use with POPS\",\n        \"id\": \"cue2pops\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"cue2pops\"\n      },\n      {\n        \"index\": \"14\",\n        \"title\": \"curlftpfs\",\n        \"description\": \"Mount ftp server as a filesystem\",\n        \"id\": \"curlftpfs\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"curlftpfs\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"15\",\n        \"title\": \"deluge\",\n        \"description\": \"Deluge Web BitTorrent Client\",\n        \"id\": \"deluge\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"deluge\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"16\",\n        \"title\": \"dexdrive\",\n        \"description\": \"Dex Drive block devices\",\n        \"id\": \"dexdrive\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"dexdrive\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"17\",\n        \"title\": \"discimagecreator\",\n        \"description\": \"Disc Image Creator\",\n        \"id\": \"disc-image-creator\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"disc-image-creator\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"18\",\n        \"title\": \"documentation\",\n        \"description\": \"Install RetroNAS documentation\",\n        \"id\": \"documentation\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"doc\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"19\",\n        \"title\": \"dreampi\",\n        \"description\": \"dialup solution for dreamcast\",\n        \"id\": \"dreampi\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"dreampi\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"20\",\n        \"title\": \"emudeck-cifs\",\n        \"description\": \"Samba for EmuDeck\",\n        \"id\": \"emudeck-cifs\",\n        \"prompt\": \"Install\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"emudeck_cifs\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"21\",\n        \"title\": \"emuelec\",\n        \"description\": \"EmuElec CIFS config\",\n        \"id\": \"emuelec-cifs\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"emuelec_cifs\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"22\",\n        \"title\": \"etherdfs\",\n        \"description\": \"EtherDFS a lightweight layer 2 network file sharing for DOS\",\n        \"id\": \"etherdfs\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"etherdfs\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"23\",\n        \"title\": \"ethflopd\",\n        \"description\": \"lightweight layer 2 network floppy emulator for DOS\",\n        \"id\": \"ethflopd\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"ethflopd\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"24\",\n        \"title\": \"extract-xiso\",\n        \"description\": \"XISO Tool\",\n        \"id\": \"extract-xiso\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"extract-xiso\"\n      },\n      {\n        \"index\": \"25\",\n        \"title\": \"far2l\",\n        \"description\": \"far2l File Manager\",\n        \"id\": \"far2l\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"far2l\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"27\",\n        \"title\": \"fsp\",\n        \"description\": \"Nintendo GameCube + Swiss FSP server\",\n        \"id\": \"fsp\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"fsp\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"28\",\n        \"title\": \"gogrepo\",\n        \"description\": \"Download your GOG game installers\",\n        \"id\": \"gogrepo\",\n        \"prompt\": \"\",\n        \"type\": \"modal\",\n        \"group\": \"\",\n        \"command\": \"gogrepo\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"29\",\n        \"title\": \"hb-store-cdn\",\n        \"description\": \"Ps4 Homebrew Store CDN\",\n        \"id\": \"hbstorecdn\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"hbstorecdn\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"30\",\n        \"title\": \"hdl-dump\",\n        \"description\": \"Sony PS2 HDD Game management\",\n        \"id\": \"hdldump\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"hdldump\"\n      },\n      {\n        \"index\": \"31\",\n        \"title\": \"hdparm\",\n        \"description\": \"Manage hdd standby mode\",\n        \"id\": \"hdparm\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"hdparm\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"32\",\n        \"title\": \"kermit\",\n        \"description\": \"kermit (server mode)\",\n        \"id\": \"kermit\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"kermit\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"33\",\n        \"title\": \"laptop-ao\",\n        \"description\": \"Disable hibernate/monitoring of laptop lid\",\n        \"id\": \"laptop-ao\",\n        \"prompt\": \"Install\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"disable-laptop-lid\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"34\",\n        \"title\": \"lighttpd\",\n        \"description\": \"HTTP/Web server\",\n        \"id\": \"lighttpd\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"lighttpd\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"35\",\n        \"title\": \"linux-gadgets\",\n        \"description\": \"Manage Linux OTG Gadgets\",\n        \"id\": \"linux_gadgets\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"linux-gadgets\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"36\",\n        \"title\": \"local-module\",\n        \"description\": \"Run local ansible playbooks\",\n        \"id\": \"run-local-module\",\n        \"prompt\": \"Run\",\n        \"type\": \"script-static\",\n        \"group\": \"\",\n        \"command\": \"run-local-module\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"37\",\n        \"title\": \"lynx\",\n        \"description\": \"Lynx terminal based web browser\",\n        \"id\": \"lynx\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"lynx\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"38\",\n        \"title\": \"macproxy classic\",\n        \"description\": \"Proxy for retro computers\",\n        \"id\": \"macproxy_classic\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"macproxy_classic\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"39\",\n        \"title\": \"mc\",\n        \"description\": \"Midnight Commander (Norton clone)\",\n        \"id\": \"mc\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"mc\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"40\",\n        \"title\": \"megatools\",\n        \"description\": \"command line client application for Mega\",\n        \"id\": \"megatools\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"megatools\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"41\",\n        \"title\": \"minicom\",\n        \"description\": \"minicom terminal\",\n        \"id\": \"minicom\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"minicom\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"42\",\n        \"title\": \"mister\",\n        \"description\": \"MiSTer FPGA CIFS config\",\n        \"id\": \"mister_cifs\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"samba\",\n        \"command\": \"mister_cifs\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"43\",\n        \"title\": \"mystic\",\n        \"description\": \"Mystic BBS\",\n        \"id\": \"mysticbbs\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"mysticbbs\",\n        \"command\": \"mysticbbs\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"44\",\n        \"title\": \"nabu\",\n        \"description\": \"NABU Internet Adapter\",\n        \"id\": \"nabu\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"nabu\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"45\",\n        \"title\": \"nbd client\",\n        \"description\": \"Network Block Device Client\",\n        \"id\": \"nbd-client\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"nbd-client\"\n      },\n      {\n        \"index\": \"46\",\n        \"title\": \"netatalk4\",\n        \"description\": \"Apple AFP file sharing / AppleTalk, etc\",\n        \"id\": \"netatalk4\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"netatalk\",\n        \"command\": \"netatalk\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"47\",\n        \"title\": \"netdrive\",\n        \"description\": \"mTCP Netdrive\",\n        \"id\": \"mtcp-netdrive\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"mtcp-netdrive\",\n        \"command\": \"mtcp-netdrive\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"48\",\n        \"title\": \"netlink\",\n        \"description\": \"Sega Saturn Netlink tunneling service\",\n        \"id\": \"netlink\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"netlink\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"49\",\n        \"title\": \"nfs\",\n        \"description\": \"NFS versions 2, 3 and 4\",\n        \"id\": \"nfs\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"nfs\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"50\",\n        \"title\": \"nginx\",\n        \"description\": \"Install nginx with directory listing enabled\",\n        \"id\": \"nginx\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"nginx\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"51\",\n        \"title\": \"ntp\",\n        \"description\": \"Network Time Protocol Server\",\n        \"id\": \"ntp\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"ntp\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"52\",\n        \"title\": \"open-iscsi\",\n        \"description\": \"iSCSI support\",\n        \"id\": \"open_iscsi\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"open-iscsi\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"53\",\n        \"title\": \"openssh\",\n        \"description\": \"SSH/SFTP/SCP Secure Shell command line and file transfer\",\n        \"id\": \"openssh\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"openssh\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"54\",\n        \"title\": \"pfsshell/pfsfuse\",\n        \"description\": \"PS2 HDD filesystem tools\",\n        \"id\": \"pfsshell\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"pfsshell\"\n      },\n      {\n        \"index\": \"55\",\n        \"title\": \"pi1541\",\n        \"description\": \"Setup a pi1541 SD card\",\n        \"id\": \"pi1541\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"pi1541\"\n      },\n      {\n        \"index\": \"56\",\n        \"title\": \"piscsi\",\n        \"description\": \"PISCSI project\",\n        \"id\": \"piscsi\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"piscsi\"\n      },\n      {\n        \"index\": \"57\",\n        \"title\": \"playStation 3\",\n        \"description\": \"ps3netsrv for CFW/HEN + webMAN-MOD\",\n        \"id\": \"\",\n        \"prompt\": \"ps3netsrv\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"ps3netsrv\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"58\",\n        \"title\": \"proftpd\",\n        \"description\": \"FTP, File Transfer Protocol file sharing\",\n        \"id\": \"proftpd\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"proftpd\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"59\",\n        \"title\": \"ps2\",\n        \"description\": \"OpenPS2Loader SMB config\",\n        \"id\": \"ps2_openps2loader\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"samba\",\n        \"command\": \"ps2_openps2loader\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"60\",\n        \"title\": \"pygopherd\",\n        \"description\": \"Gopher protocol server\",\n        \"id\": \"pygopherd\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"pygopherd\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"61\",\n        \"title\": \"ras\",\n        \"description\": \"Retro AIM/ICQ Server\",\n        \"id\": \"retroaimserver\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"retroaimserver\",\n        \"command\": \"retroaimserver\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"62\",\n        \"title\": \"rclone\",\n        \"description\": \"rclone + webui\",\n        \"id\": \"rclone\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"rclone\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"63\",\n        \"title\": \"recalbox\",\n        \"description\": \"Recalbox CIFS config\",\n        \"id\": \"recalbox-cifs\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"recalbox_cifs\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"64\",\n        \"title\": \"redumper\",\n        \"description\": \"Redumper project\",\n        \"id\": \"redumper\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"redumper\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"65\",\n        \"title\": \"retroarch-cifs\",\n        \"description\": \"RetroArch Based Systems CIFS config\",\n        \"id\": \"retroarch-cifs\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"retroarch_cifs\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"66\",\n        \"title\": \"retrodeck-cifs\",\n        \"description\": \"Samba for RetroDeck\",\n        \"id\": \"retrodeck-cifs\",\n        \"prompt\": \"Install\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"retrodeck_cifs\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"67\",\n        \"title\": \"rom import\",\n        \"description\": \"Import roms via Smokemonster SMDBs\",\n        \"id\": \"romimport\",\n        \"prompt\": \"Open\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"romimport\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"68\",\n        \"title\": \"sabretools\",\n        \"description\": \"Dat Manager (x86_64 only)\",\n        \"id\": \"sabretools\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"sabretools\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"69\",\n        \"title\": \"samba\",\n        \"description\": \"LANMan, NTLMv1/v2, NetBIOS, SMB1/2/3, CIFS file sharing\",\n        \"id\": \"samba\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"samba\",\n        \"command\": \"samba\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"70\",\n        \"title\": \"seaweedfs\",\n        \"description\": \"SeaweedFS for local S3 storage\",\n        \"id\": \"seaweedfs\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"seaweedfs\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"71\",\n        \"title\": \"sidecart\",\n        \"description\": \"Atari ST sidecart support\",\n        \"id\": \"sidecart\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"atarist-sidecart\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"72\",\n        \"title\": \"sslcert\",\n        \"description\": \"Manage a self-signed ssl certificate\",\n        \"id\": \"sslcert\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"sslcert\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"73\",\n        \"title\": \"syncthing\",\n        \"description\": \"File sync tool\",\n        \"id\": \"syncthing\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"syncthing\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"74\",\n        \"title\": \"tcpser\",\n        \"description\": \"Hayes modem emulator\",\n        \"id\": \"tcpser\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"tcpser\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"75\",\n        \"title\": \"telnet\",\n        \"description\": \"unencrypted remote access shell\",\n        \"id\": \"telnet\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"telnet\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"76\",\n        \"title\": \"tftpd-hpa\",\n        \"description\": \"TFTP, Trivial File Transfer Protocol file sharing\",\n        \"id\": \"tftpd-hpa\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"tftpd-hpa\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"77\",\n        \"title\": \"tnfs\",\n        \"description\": \"Atari 8-bit and ZX Spectrum\",\n        \"id\": \"tnfs\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"tnfs\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"78\",\n        \"title\": \"troubleshooting\",\n        \"description\": \"Troubleshooting tools, iperf3, tcpdump, ethtool, smarttools, etc\",\n        \"id\": \"troubleshooting\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"troubleshooting\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"79\",\n        \"title\": \"ucon64\",\n        \"description\": \"Multipurpose Swiss Army Knife Backup/Copier tool\",\n        \"id\": \"ucon64\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"ucon64\"\n      },\n      {\n        \"index\": \"80\",\n        \"title\": \"udpbd\",\n        \"description\": \"PS2 block device service\",\n        \"id\": \"ps2_udpbd\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"ps2_udpbd\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"81\",\n        \"title\": \"waybackproxy\",\n        \"description\": \"Retrocomputer web proxy for Wayback and OOcities\",\n        \"id\": \"waybackproxy\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"waybackproxy\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"82\",\n        \"title\": \"webone\",\n        \"description\": \"HTTP 1.x proxy for a HTTP 2.x world\",\n        \"id\": \"webone\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"webone\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"83\",\n        \"title\": \"wrp\",\n        \"description\": \"Proxy for retro computers, uses image maps\",\n        \"id\": \"wrp\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"wrp\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"84\",\n        \"title\": \"x360\",\n        \"description\": \"Microsoft XBox360 SMB config\",\n        \"id\": \"xbox360\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"samba\",\n        \"command\": \"xbox360\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"85\",\n        \"title\": \"xboxmanager\",\n        \"description\": \"Experimental xbox manager (cockpit)\",\n        \"id\": \"xboxmanager\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"xboxmanager\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"86\",\n        \"title\": \"xlink-kai\",\n        \"description\": \"XLink Kai Gaming Network\",\n        \"id\": \"xlinkkai\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"xlinkkai\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"87\",\n        \"title\": \"ytree\",\n        \"description\": \"Ytree File Manager (Xtree clone)\",\n        \"id\": \"ytree\",\n        \"prompt\": \"\",\n        \"type\": \"install\",\n        \"group\": \"\",\n        \"command\": \"ytree\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"88\",\n        \"title\": \"zterm\",\n        \"description\": \"Zmodem transfer suite\",\n        \"id\": \"zterm\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"\",\n        \"command\": \"zterm\",\n        \"args\": \"\"\n      },\n      {\n        \"index\": \"89\",\n        \"title\": \"romm\",\n        \"description\": \"RomM CIFS config\",\n        \"id\": \"romm_cifs\",\n        \"prompt\": \"\",\n        \"type\": \"dialog\",\n        \"group\": \"samba\",\n        \"command\": \"romm_cifs\",\n        \"args\": \"\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "config/menu/lighttpd.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"lighttpd\",\n        \"description\": \"Install lighttpd\",\n        \"id\":\"lighttpd\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install lighttpd\",\n                \"id\":\"lighttpd\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"lighttpd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"lighttpd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"lighttpd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"lighttpd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"NEEEDED.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/linux-gadgets.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"Linux Gadgets\",\n        \"description\": \"Manage Linux OTG Gadgets\",\n        \"id\":\"linux-gadgets\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install Gadget support\",\n                \"id\":\"linux-gadgets\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"linux-gadgets\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Mass Storage\",\n                \"description\": \"Manage g_mass_storage gadgets\",\n                \"id\":\"gadget-mass-storage-manage\",\n                \"prompt\": \"Manage\",\n                \"type\": \"script\",\n                \"group\":\"\",\n                \"command\": \"gadget-mass-storage-manage\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Start\",\n                \"description\": \"Start gadget\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"retronas-gadget.path\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop gadget\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"retronas-gadget.path\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"06\",\n                \"title\": \"Status\",\n                \"description\": \"Query gadget\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_status\",\n                \"group\":\"\",\n                \"command\": \"retronas-gadget*\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/litch.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"litch\",\n        \"description\": \"itch.io leech script||WARNING: This tool is in an alpha state,\\nsee https://github.com/sairuk/gogrepo||Please choose a task\",\n        \"id\":\"litch\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Login\",\n                \"description\": \"Configure your itch.io credentials\",\n                \"id\":\"litch-login\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"litch\",\n                \"command\": \"litch_login\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Claim\",\n                \"description\": \"Claim bundle purchases\",\n                \"id\":\"litch-claim\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"litch\",\n                \"command\": \"litch_claim\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Download\",\n                \"description\": \"Download your purchases\",\n                \"id\":\"litch-download\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"litch\",\n                \"command\": \"litch_download\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"Down/Clean\",\n                \"description\": \"Download(+clean) purchases\",\n                \"id\":\"litch-download-clean\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"litch\",\n                \"command\": \"litch_download_clean\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/lynx.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"lynx\",\n        \"description\": \"Lynx terminal based web browser\",\n        \"id\":\"lynx\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"02\",\n                \"title\": \"Install\",\n                \"description\": \"Lynx terminal based web browser\",\n                \"id\": \"lynx\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\": \"\",\n                \"command\": \"lynx\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Run\",\n                \"description\": \"Run the Lynx browser\",\n                \"id\":\"lynx_run\",\n                \"prompt\": \"\",\n                \"type\": \"command\",\n                \"group\":\"\",\n                \"command\": \"lynx\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/macproxy_classic.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"macproxy classic\",\n        \"description\": \"Install macproxy classic\",\n        \"id\":\"macproxy_classic\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install macproxy classic\",\n                \"id\":\"macproxy_classic\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"macproxy_classic\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"macproxy\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"macproxy\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"macproxy\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"macproxy.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config/menu/main.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"Main\",\n        \"description\": \"Welcome to RetroNAS|-------------------|Providing network storage, utilities and tools for different retro computers and consoles||Configure RetroNAS before installing any tools.\\\\(see Config menu\\\\)\",\n        \"id\":\"main\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Exit\",\n                \"description\": \"So long and thanks for all the bits\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Config\",\n                \"description\": \"Change Global Settings\",\n                \"id\":\"config\",\n                \"prompt\": \"\",\n                \"type\": \"dialog\",\n                \"group\":\"\",\n                \"command\": \"config\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"03\",\n                \"title\": \"Install\",\n                \"description\": \"The fun place, for things\",\n                \"id\":\"install\",\n                \"prompt\": \"\",\n                \"type\": \"dialog\",\n                \"group\":\"\",\n                \"command\": \"install\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"50\",\n                \"title\": \"Experimental\",\n                \"description\": \"there be dragons here!\",\n                \"id\":\"experimental\",\n                \"prompt\": \"\",\n                \"type\": \"dialog\",\n                \"group\":\"\",\n                \"command\": \"experimental\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"98\",\n                \"title\": \"Update\",\n                \"description\": \"Update core RetroNAS features\",\n                \"id\":\"update\",\n                \"prompt\": \"\",\n                \"type\": \"dialog\",\n                \"group\":\"\",\n                \"command\": \"update\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"View project documentation\",\n                \"id\":\"documentation\",\n                \"prompt\": \"\",\n                \"type\": \"dialog\",\n                \"group\":\"\",\n                \"command\": \"doc\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config/menu/minicom.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"minicom\",\n        \"description\": \"Please select an option\",\n        \"id\":\"minicom\",\n        \"prompt\": \"\",\n        \"type\": \"modal\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install minicom application\",\n                \"id\":\"minicom\",\n                \"prompt\": \"Install\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"minicom\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"03\",\n                \"title\": \"Start\",\n                \"description\": \"Run minicom\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"\",\n                \"command\": \"minicom\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Config\",\n                \"description\": \"Start minicom in config mode\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"\",\n                \"command\": \"minicom-config\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"minicom.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config/menu/mister-organize.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"MiSTer Organize Tool\",\n        \"description\": \"MiSTer Organize is a project providing a curated list of software compatible with the MiSTer project||Place your software in to the romimport folder and run the tool; if identified the software will be moved into the RomRoot folder, you may then move the software onto your main MiSTer share/sd etc\",\n        \"id\":\"mister-organize-menu\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install\",\n                \"id\":\"mister-organize-install\",\n                \"prompt\": \"Install\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"mister-organize\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Run\",\n                \"description\": \"Run MiSTer Organize\",\n                \"id\":\"mister-organize\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"\",\n                \"command\": \"mister-organize\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"MiSTer-Organize.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config/menu/mister_cifs.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"MiSTer\",\n        \"description\": \"Install the MiSTer Samba service, enable/disable the auto disk updater or run the updater manually\",\n        \"id\":\"mister-cifs\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install Samba for MiSTer support\",\n                \"id\":\"mister-cifs\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"mister_cifs\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Enable\",\n                \"description\": \"Enable auto dir updates\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_enable_start\",\n                \"group\":\"\",\n                \"command\": \"retronas-mister-dirs.timer\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Disable\",\n                \"description\": \"Disable auto dir updater\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_disable_stop\",\n                \"group\":\"\",\n                \"command\": \"retronas-mister-dirs.timer\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"Status\",\n                \"description\": \"Query auto dir updater\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_status\",\n                \"group\":\"\",\n                \"command\": \"retronas-mister-dirs*\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"06\",\n                \"title\": \"Run\",\n                \"description\": \"Run directory updater now\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start_follow\",\n                \"group\":\"\",\n                \"command\": \"retronas-mister-dirs.service\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"MiSTer-FPGA.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/mtcp-netdrive.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"mTCP Netdrive\",\n        \"description\": \"Install mTCP Netdrive||To configure disks, access the terminal and run the create/image options as per project documentation\",\n        \"id\":\"mtcp-netdrive\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install mTCP Netdrive\",\n                \"id\":\"mtcp-netdrive\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"mtcp-netdrive\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Console\",\n                \"description\": \"Connect mTCP Netdrive console (Ctrl+A,D to detach)\",\n                \"id\":\"mtcp-netdrive\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"\",\n                \"command\": \"mtcp-netdrive\",\n                \"args\": \"console\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start mis service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_enable_start\",\n                \"group\":\"\",\n                \"command\": \"mtcp-netdrive\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"mtcp-netdrive\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_disable_stop\",\n                \"group\":\"\",\n                \"command\": \"mtcp-netdrive\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"mtcp-netdrive.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/mysticbbs.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"Mystic BBS\",\n        \"description\": \"Install Mystic BBS||NOTE: BBS software configuration is a manual experience, after installation create user in local mode, configure your system. Once configuration is complete start the server\",\n        \"id\":\"mysticbbs\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install Mystic BBS\",\n                \"id\":\"mysticbbs\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"mysticbbs\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Local\",\n                \"description\": \"Run Mystic BBS local mode\",\n                \"id\":\"mysticbbs-local\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"\",\n                \"command\": \"mysticbbs\",\n                \"args\": \"local\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Config\",\n                \"description\": \"Mystic BBS configuration\",\n                \"id\":\"mysticbbs-config\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"\",\n                \"command\": \"mysticbbs\",\n                \"args\": \"config\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"File Areas\",\n                \"description\": \"Update RetroNAS file areas in Mystic\",\n                \"id\":\"mysticbbs-filearea\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"\",\n                \"command\": \"mysticbbs\",\n                \"args\": \"filearea\"\n            },\n            {\n                \"index\":\"06\",\n                \"title\": \"Upload\",\n                \"description\": \"Trigger mass uploads in Mystic\",\n                \"id\":\"mysticbbs-upload\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"\",\n                \"command\": \"mysticbbs\",\n                \"args\": \"upload\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start mis service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_enable_start\",\n                \"group\":\"\",\n                \"command\": \"mysticbbs-mis\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"mysticbbs-mis\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_disable_stop\",\n                \"group\":\"\",\n                \"command\": \"mysticbbs-mis\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"mysticbbs.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/nabu.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"NABU Internet Adapter\",\n        \"description\": \"Manage the NABU Internet Adapter\",\n        \"id\":\"naubia\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install the NABU Internet Adapter software\",\n                \"id\":\"nabu-software\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"nabu\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Open\",\n                \"description\": \"Open the NABU Internet Adapter\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"\",\n                \"command\": \"nabu\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"NABU.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/netatalk-legacy.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"netatalk-legacy\",\n        \"description\": \"Install legacy netatalk, NOTE: No longer supported, use Netatalk 4\",\n        \"id\":\"netatalk_legacy_install\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install v2\",\n                \"description\": \"Install netatalk2\",\n                \"id\":\"netatalk2\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"netatalk2\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Install v3\",\n                \"description\": \"Install netatalk3\",\n                \"id\":\"netatalk3\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"netatalk3\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config/menu/netatalk.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"netatalk4\",\n        \"description\": \"Install netatalk4\",\n        \"id\":\"netatalk4_install\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install netatalk4\",\n                \"id\":\"netatalk4\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"netatalk4\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start Netatalk service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"netatalk\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query Netatalk status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"netatalk\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the Netatalk service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"netatalk\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"30\",\n                \"title\": \"Start\",\n                \"description\": \"Start AppleTalk service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"atalkd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"31\",\n                \"title\": \"Status\",\n                \"description\": \"Query AppleTalk status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"atalkd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"32\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the AppleTalk service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"atalkd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"98\",\n                \"title\": \"Legacy\",\n                \"description\": \"Install legacy versions of netatalk\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"dialog\",\n                \"group\":\"\",\n                \"command\": \"netatalk-legacy\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"Netatalk.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/netlink.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"netlink\",\n        \"description\": \"Install netlink\",\n        \"id\":\"netlink\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install netlink\",\n                \"id\":\"netlink\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"netlink\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"netlink\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"netlink\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"netlink\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"Netlink.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/netmount.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"netmount\",\n        \"description\": \"Install netmount\",\n        \"id\":\"netmount\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install NetMount\",\n                \"id\":\"netmount\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"netmount\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start RetroNAS NetMount service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"netmount-drives-retronas\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query RetroNAS NetMount service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"netmount-drives-retronas\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop RetroNAS NetMount service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"netmount-drives-retronas\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"23\",\n                \"title\": \"Build\",\n                \"description\": \"Run the NetMount service builder script\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"\",\n                \"command\": \"netmount-confman\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"NetMount.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config/menu/network-manual.json",
    "content": "{\n    \"menu\":    \n    {\n        \"title\": \"network\",\n        \"description\": \"Manually install networking tools\",\n        \"id\":\"network\",\n        \"prompt\": \"Install\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\"\n            },\n            {\n                \"index\": \"02\",\n                \"title\": \"firewalld\",\n                \"description\": \"Basic firewalld\", \n                \"id\":\"firewalld\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"firewalld\"\n            },\n            {\n                \"index\": \"03\",\n                \"title\": \"dnsmasq\",\n                \"description\": \"Basic dnsmasq\", \n                \"id\":\"dnsmasq\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"dnsmasq\"\n            },\n            {\n                \"index\": \"04\",\n                \"title\": \"hostapd\",\n                \"description\": \"hostapd\", \n                \"id\":\"hostapd\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"hostapd\"\n            },\n            {\n                \"index\": \"05\",\n                \"title\": \"firewalld-zoning\",\n                \"description\": \"retro (eth0)/modern (wlan0) zone\", \n                \"id\":\"firewalld-zones\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"firewalld-zones\"\n            },\n            {\n                \"index\": \"06\",\n                \"title\": \"dnsmasq-retro\",\n                \"description\": \"DHCP, DNS support for retro zone\", \n                \"id\":\"dnsmasq-retro\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"dnsmasq-retro\"\n            },\n            {\n                \"index\": \"07\",\n                \"title\": \"dhcpcd\",\n                \"description\": \"DHCP client\", \n                \"id\":\"dhcpcd\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"dhcpcd\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/network-presets.json",
    "content": "{\n    \"menu\":    \n    {\n        \"title\": \"network\",\n        \"description\": \"Install networking using pre-selected modes of operation\",\n        \"id\":\"network\",\n        \"prompt\": \"Install\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\"\n            },\n            {\n                \"index\": \"02\",\n                \"title\": \"preview-zoned\",\n                \"description\": \"Preview the zoned network diagram\", \n                \"id\":\"network-example-presets-zoned\",\n                \"prompt\": \"\",\n                \"type\": \"script-static\",\n                \"group\":\"\",\n                \"command\": \"network-example-presets-zoned\"\n            },\n            {\n                \"index\": \"03\",\n                \"title\": \"preview-standalone\",\n                \"description\": \"Preview the standalone network diagram\", \n                \"id\":\"network-example-presets-standalone\",\n                \"prompt\": \"\",\n                \"type\": \"script-static\",\n                \"group\":\"\",\n                \"command\": \"network-example-presets-standalone\"\n            },\n            {\n                \"index\": \"04\",\n                \"title\": \"net-ethernet-dhcp\",\n                \"description\": \"Install the ethernet dhcp network configuration\", \n                \"id\":\"network-presets-ethernet-dhcp\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"network-presets-ethernet-dhcp\"\n            },\n            {\n                \"index\": \"05\",\n                \"title\": \"net-zoned\",\n                \"description\": \"Install the zoned preset network configuration\", \n                \"id\":\"network-presets-zoned\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"network-presets-zoned\"\n            },\n            {\n                \"index\": \"06\",\n                \"title\": \"net-standalone\",\n                \"description\": \"Install the zoned preset network configuration\", \n                \"id\":\"network-presets-standalone\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"network-presets-standalone\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/network-setup-modern.json",
    "content": "{\n    \"menu\":    \n    {\n        \"title\": \"network\",\n        \"description\": \"Setup the network for use when installing the network services\",\n        \"id\":\"network\",\n        \"prompt\": \"Install\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"modern-nic\",\n                \"description\": \"Set the interface for the Modern network\",\n                \"id\":\"set-retronas-net-modern-nic\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"set-retronas-net-modern-nic\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/network-setup-retro.json",
    "content": "{\n    \"menu\":    \n    {\n        \"title\": \"retronet\",\n        \"description\": \"Setup the retro network\",\n        \"id\":\"network\",\n        \"prompt\": \"Install\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"retro-nic\",\n                \"description\": \"Set the interface for the Retro network\",\n                \"id\":\"set-retronas-net-retro-nic\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"set-retronas-net-retro-nic\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"dns1\",\n                \"description\": \"Set the primary DNS upstream server\",\n                \"id\":\"set-retronas-net-upstream-dns1\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"set-retronas-net-upstream-dns1\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"dns2\",\n                \"description\": \"Set the secondary DNS upstream server\",\n                \"id\":\"set-retronas-net-upstream-dns2\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"set-retronas-net-upstream-dns2\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"dhcp-ntp\",\n                \"description\": \"Set ntp server for the retro network\",\n                \"id\":\"set-retronas-net-retro-ntp\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"set-retronas-net-retro-ntp\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"06\",\n                \"title\": \"dhcp-iprange\",\n                \"description\": \"Set the ip range for the retro network\",\n                \"id\":\"set-retronas-net-retro-dhcprange\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"set-retronas-net-retro-dhcprange\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"07\",\n                \"title\": \"dhcp-gateway\",\n                \"description\": \"Set the gateway for the retro network\",\n                \"id\":\"set-retronas-net-retro-router\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"set-retronas-net-retro-router\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"08\",\n                \"title\": \"retro-ip\",\n                \"description\": \"Set the ip address for the retro network\",\n                \"id\":\"set-retronas-net-retro-ip\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"set-retronas-net-retro-ip\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"09\",\n                \"title\": \"retro-subnet\",\n                \"description\": \"Set the subnet for the retro network\",\n                \"id\":\"set-retronas-net-retro-subnet\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"set-retronas-net-retro-subnet\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"10\",\n                \"title\": \"retro-dns\",\n                \"description\": \"Set the dns server for the retro network\",\n                \"id\":\"set-retronas-net-retro-dns\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"set-retronas-net-retro-dns\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/network-setup-wifiap.json",
    "content": "{\n    \"menu\":    \n    {\n        \"title\": \"wifi ap\",\n        \"description\": \"Setup the wifi access point settings, a default password of \\\"retronas\\\" is configured at install make sure you change this\",\n        \"id\":\"wifiip\",\n        \"prompt\": \"Configure\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"wifi-show-pwd\",\n                \"description\": \"Show the current wifi password\",\n                \"id\":\"wifi-show-pw\",\n                \"prompt\": \"\",\n                \"type\": \"script-static\",\n                \"group\":\"\",\n                \"command\": \"wifi-show-passwd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"wifi-nic\",\n                \"description\": \"Set the wifi interface\",\n                \"id\":\"set-retronas-net-wifi-interface\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"set-retronas-net-wifi-interface\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"wifi-ssid\",\n                \"description\": \"Set the wifi ssid\",\n                \"id\":\"set-retronas-net-wifi-ssid\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"set-retronas-net-wifi-ssid\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"wifi-cc\",\n                \"description\": \"Set the wifi country code\",\n                \"id\":\"set-retronas-net-wifi-countrycode\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"set-retronas-net-wifi-countrycode\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"06\",\n                \"title\": \"wifi-hwmode\",\n                \"description\": \"Set the wifi hardware mode\",\n                \"id\":\"set-retronas-net-wifi-hwmode\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"set-retronas-net-wifi-hwmode\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"07\",\n                \"title\": \"wifi-ip\",\n                \"description\": \"Set the ip address for the wifi network\",\n                \"id\":\"set-retronas-net-wifi-ip\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"set-retronas-net-wifi-ip\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"08\",\n                \"title\": \"wifi-channel\",\n                \"description\": \"Set the wifi channel\",\n                \"id\":\"set-retronas-net-wifi-channel\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"set-retronas-net-wifi-channel\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"09\",\n                \"title\": \"wifi-passwd\",\n                \"description\": \"Configure wifi password\",\n                \"id\":\"wifi-password\",\n                \"prompt\": \"\",\n                \"type\": \"modal\",\n                \"group\":\"\",\n                \"command\": \"wifi_password\"\n            },\n            {\n                \"index\":\"10\",\n                \"title\": \"wifi-dns\",\n                \"description\": \"Set the dns servers\",\n                \"id\":\"set-retronas-net-wifi-dns\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"set-retronas-net-wifi-dns\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"11\",\n                \"title\": \"wifi-dhcprange\",\n                \"description\": \"Set the ip range for the wifi network\",\n                \"id\":\"set-retronas-net-wifi-dhcprange\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"set-retronas-net-wifi-dhcprange\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"12\",\n                \"title\": \"wifi-gateway\",\n                \"description\": \"Set the gateway for the wifi network\",\n                \"id\":\"set-retronas-net-wifi-router\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"set-retronas-net-wifi-router\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"13\",\n                \"title\": \"wifi-subnet\",\n                \"description\": \"Set the subnet for the wifi network\",\n                \"id\":\"set-retronas-net-wifi-subnet\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"set-retronas-net-wifi-subnet\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"14\",\n                \"title\": \"wifi-ntp\",\n                \"description\": \"Set the subnet for the wifi network\",\n                \"id\":\"set-retronas-net-wifi-ntp\",\n                \"prompt\": \"\",\n                \"type\": \"dialog_input\",\n                \"group\":\"\",\n                \"command\": \"set-retronas-net-wifi-ntp\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/network-setup.json",
    "content": "{\n    \"menu\":    \n    {\n        \"title\": \"network\",\n        \"description\": \"Setup the network for use when installing the network services\",\n        \"id\":\"network\",\n        \"prompt\": \"Install\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"retro-net\",\n                \"description\": \"Set up the Retro network\",\n                \"id\":\"network-setup-retro\",\n                \"prompt\": \"\",\n                \"type\": \"dialog\",\n                \"group\":\"\",\n                \"command\": \"network-setup-retro\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"12\",\n                \"title\": \"modern-net\",\n                \"description\": \"Set up the Modern network\",\n                \"id\":\"network-setup-modern\",\n                \"prompt\": \"\",\n                \"type\": \"dialog\",\n                \"group\":\"\",\n                \"command\": \"network-setup-modern\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"wifi-ap\",\n                \"description\": \"Set up the retronas wifi access point\",\n                \"id\":\"network-setup-wifiap\",\n                \"prompt\": \"\",\n                \"type\": \"dialog\",\n                \"group\":\"\",\n                \"command\": \"network-setup-wifiap\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/network.json",
    "content": "{\n    \"menu\":    \n    {\n        \"title\": \"network\",\n        \"description\": \"HIGHLY EXPERIMENTAL||This menu will allow you to install firewall, dhcp and dns services. You may install these services as vanilla or install the retro/modern zones to provide isolation.||The default net is 10.99.1.0/23 for retro on eth0 while the modern net is connected over wlan0.||Traffic is forwarded from eth0 to wlan0 for internet access\",\n        \"id\":\"network\",\n        \"prompt\": \"Install\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\"\n            },\n            {\n                \"index\": \"02\",\n                \"title\": \"manual\",\n                \"description\": \"Tools for manual network setup\", \n                \"id\":\"network-manual\",\n                \"prompt\": \"\",\n                \"type\": \"dialog\",\n                \"group\":\"\",\n                \"command\": \"network-manual\"\n            },\n            {\n                \"index\": \"03\",\n                \"title\": \"presets\",\n                \"description\": \"Network preset setups\", \n                \"id\":\"network-presets\",\n                \"prompt\": \"\",\n                \"type\": \"dialog\",\n                \"group\":\"\",\n                \"command\": \"network-presets\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"config\",\n                \"description\": \"Configure networking\",\n                \"id\":\"network-setup\",\n                \"prompt\": \"\",\n                \"type\": \"dialog\",\n                \"group\":\"\",\n                \"command\": \"network-setup\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"Networking.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config/menu/nginx.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"nginx\",\n        \"description\": \"Install nginx\",\n        \"id\":\"nginx\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install nginx\",\n                \"id\":\"nginx\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"nginx\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"nginx\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"nginx\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"nginx\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"HTTP.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config/menu/ntp.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"ntp\",\n        \"description\": \"Install ntp\",\n        \"id\":\"ntp\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install ntp\",\n                \"id\":\"ntp\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"ntp\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"openntpd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"openntpd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"openntpd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"NTP.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config/menu/open-iscsi.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"iSCSI options\",\n        \"description\": \"Manage iSCSI targets\",\n        \"id\":\"open-iscsi\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install iSCSI support\",\n                \"id\":\"open-iscsi\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"open-iscsi\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Targets\",\n                \"description\": \"List and login to iSCSI targets\",\n                \"id\":\"iscsi-manager-target-login\",\n                \"prompt\": \"Login\",\n                \"type\": \"script\",\n                \"group\":\"\",\n                \"command\": \"iscsi-manager-target-login\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Start\",\n                \"description\": \"Start gadget\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"open-iscsi\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop gadget\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"open-iscsi\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"06\",\n                \"title\": \"Status\",\n                \"description\": \"Query gadget\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_status\",\n                \"group\":\"\",\n                \"command\": \"open-iscsi\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"iSCSI.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/openssh.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"openssh\",\n        \"description\": \"Install openssh\",\n        \"id\":\"openssh\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install openssh\",\n                \"id\":\"openssh\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"openssh\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"ssh\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"ssh\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"ssh\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"ssh.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config/menu/pi1541.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"pi1541\",\n        \"description\": \"Assists with setting up a pi1541 sd card device, provide a FAT32 formatted device and run setup\",\n        \"id\":\"pi1541\",\n        \"prompt\": \"\",\n        \"type\": \"modal\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install pi1541 setup script\",\n                \"id\":\"pi151\",\n                \"prompt\": \"Install\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"pi1541\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"03\",\n                \"title\": \"Setup\",\n                \"description\": \"Run the setup script\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"\",\n                \"command\": \"pi1541\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"PI1541.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/piscsi.json",
    "content": "{\n    \"menu\":\n    {\n        \"title\": \"piscsi\",\n        \"description\": \"Please select an option\",\n        \"id\":\"piscsi\",\n        \"prompt\": \"\",\n        \"type\": \"modal\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install piscsi standalone - (builds FULLSPEC)\",\n                \"id\":\"piscsi\",\n                \"prompt\": \"Install\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"piscsi\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Standard\",\n                \"description\": \"Build piscsi standalone - STANDARD\",\n                \"id\":\"piscsi\",\n                \"prompt\": \"Build\",\n                \"type\": \"script\",\n                \"group\":\"\",\n                \"command\": \"install_piscsi_standard\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Fullspec\",\n                \"description\": \"Build piscsi standalone - FULLSPEC\",\n                \"id\":\"piscsi_fullspec\",\n                \"prompt\": \"Install\",\n                \"type\": \"script\",\n                \"group\":\"\",\n                \"command\": \"install_piscsi\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"Start\",\n                \"description\": \"Start piscsi\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"piscsi\"\n            },\n            {\n                \"index\":\"06\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop piscsi\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"piscsi\"\n            },\n            {\n                \"index\":\"07\",\n                \"title\": \"Status\",\n                \"description\": \"Query piscsi\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_status\",\n                \"group\":\"\",\n                \"command\": \"piscsi\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"PISCSI.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config/menu/profiles.json",
    "content": "    {\n    \"menu\": \n    {\n        \"title\": \"Installation Profile(s)\",\n        \"description\": \"Available installation profiles||Profile Location: \\\"${OLDRNPATH}/config\\\"\",\n        \"id\":\"profiles\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"Profiles.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/proftpd.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"proftpd\",\n        \"description\": \"Install proftpd\",\n        \"id\":\"proftpd\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install proftpd\",\n                \"id\":\"proftpd\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"proftpd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"proftpd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"proftpd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"proftpd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"FTP.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/ps2_udpbd.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"udpbd\",\n        \"description\": \"Install the UPDDB service for PS2\",\n        \"id\":\"ps2-udpbd\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install udpbd support\",\n                \"id\":\"ps2-udpbd\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"ps2_udpbd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Enable\",\n                \"description\": \"Enable and Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_enable_start\",\n                \"group\":\"\",\n                \"command\": \"ps2_udpbd.service\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Disable\",\n                \"description\": \"Disable and stop service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_disable_stop\",\n                \"group\":\"\",\n                \"command\": \"ps2_udpbd.service\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"Status\",\n                \"description\": \"Query service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_status\",\n                \"group\":\"\",\n                \"command\": \"ps2_udpbd.service\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"06\",\n                \"title\": \"Restart\",\n                \"description\": \"Restart service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_restart\",\n                \"group\":\"\",\n                \"command\": \"ps2_udpbd.service\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"07\",\n                \"title\": \"Select\",\n                \"description\": \" Block Device (requires service restart)\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"\",\n                \"command\": \"udpbd_manager\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"UDPBD.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config/menu/ps3netsrv.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"ps3netsrv\",\n        \"description\": \"Install ps3netsrv\",\n        \"id\":\"ps3netsrv\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install ps3netsrv\",\n                \"id\":\"ps3netsrv\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"ps3netsrv\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"ps3netsrv\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"ps3netsrv\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"ps3netsrv\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"ps3netsrv.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/rclone.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"rclone\",\n        \"description\": \"Install rclone\",\n        \"id\":\"rclone\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install rclone\",\n                \"id\":\"rclone\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"rclone\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start webui service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"rclone-webui\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query webui status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"rclone-webui\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop webui service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"rclone-webui\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"rclone.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config/menu/retroaimserver.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"RAS\",\n        \"description\": \"Install Retro Aim Server||You must have configured your retro network interface address in the Network menu before installing this service\",\n        \"id\":\"retroaimserver\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install retroaimserver\",\n                \"id\":\"retroaimserver\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"retroaimserver\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"retro-aim-server\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"retro-aim-server\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"retro-aims-erver\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"retroaimserver.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/romimport.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"ROM Import Tool\",\n        \"description\": \"This uses Frederic Mahe's Python scripts and Smokemonster's SMDB databases to import ROMs.||ROMs are matched by checksum, renamed and placed into the matching directory structure via space-saving hard links.||Existing ROMs will never be overwritten.||If ROMs fail to import, it means you have a ROM with a checksum not in the database, or the system type is not yet added.||Place ROMs in the import directory above use SMB/CIFS/AFP/FTP/SCP/SFTP/whatever first.||Do you wish to proceed?\",\n        \"id\":\"romimport-menu\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install\",\n                \"id\":\"romimport\",\n                \"prompt\": \"Install\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"romimport\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Import By System\",\n                \"description\": \"Import a Specific System\",\n                \"id\":\"romimportsystem\",\n                \"prompt\": \"\",\n                \"type\": \"modal\",\n                \"group\":\"\",\n                \"command\": \"romimportsystem\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"ROM-import.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/romimportsystem.json",
    "content": "{\n    \"menu\": {\n        \"title\": \"ROM Import by System\",\n        \"description\": \"Import a specific system.\",\n        \"id\": \"romimportsystem\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\",\n                \"id\": \"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\": \"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"02\",\n                \"title\": \"Import Atari 2600\",\n                \"description\": \"\",\n                \"id\": \"romimport-a2600\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"03\",\n                \"title\": \"Import PC Engine CD Redump Supplement\",\n                \"description\": \"\",\n                \"id\": \"romimport-pcecdrs\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"04\",\n                \"title\": \"Import N64\",\n                \"description\": \"\",\n                \"id\": \"romimport-n64\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"05\",\n                \"title\": \"Import NeoGeo Pocket\",\n                \"description\": \"\",\n                \"id\": \"romimport-ngp\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\"\n            },\n            {\n                \"index\": \"06\",\n                \"title\": \"Import Sega SG-1000\",\n                \"description\": \"\",\n                \"id\": \"romimport-sg1000\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"07\",\n                \"title\": \"Import Colecovision\",\n                \"description\": \"\",\n                \"id\": \"romimport-cv\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"08\",\n                \"title\": \"Import Saturn\",\n                \"description\": \"\",\n                \"id\": \"romimport-saturn\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"09\",\n                \"title\": \"Import MegaDrive/Genesis\",\n                \"description\": \"\",\n                \"id\": \"romimport-md\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"10\",\n                \"title\": \"Import NeoGeo\",\n                \"description\": \"\",\n                \"id\": \"romimport-neogeo\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"11\",\n                \"title\": \"Import SNES/SuperFamicom\",\n                \"description\": \"\",\n                \"id\": \"romimport-snes\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"12\",\n                \"title\": \"Import Watara Supervision\",\n                \"description\": \"\",\n                \"id\": \"romimport-supervision\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"13\",\n                \"title\": \"Import PSX Redump Supplement\",\n                \"description\": \"\",\n                \"id\": \"romimport-psx\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"14\",\n                \"title\": \"Import Sega CD/Mega CD\",\n                \"description\": \"\",\n                \"id\": \"romimport-megacd\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"15\",\n                \"title\": \"Import Wonderswancolor\",\n                \"description\": \"\",\n                \"id\": \"romimport-wonderswancolor\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"16\",\n                \"title\": \"Import Master System\",\n                \"description\": \"\",\n                \"id\": \"romimport-segams\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"17\",\n                \"title\": \"Import Lynx\",\n                \"description\": \"\",\n                \"id\": \"romimport-lynx\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"18\",\n                \"title\": \"Import Gameboy Advance\",\n                \"description\": \"\",\n                \"id\": \"romimport-gba\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"19\",\n                \"title\": \"Import Gameboy Color\",\n                \"description\": \"\",\n                \"id\": \"romimport-gbc\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"20\",\n                \"title\": \"Import Vectrix\",\n                \"description\": \"\",\n                \"id\": \"romimport-vectrix\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"21\",\n                \"title\": \"Import Atari 7800\",\n                \"description\": \"\",\n                \"id\": \"romimport-a7800\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"22\",\n                \"title\": \"Import NeoGeo Pocket Color\",\n                \"description\": \"\",\n                \"id\": \"romimport-ngpc\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"23\",\n                \"title\": \"Import Gameboy\",\n                \"description\": \"\",\n                \"id\": \"romimport-gb\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"24\",\n                \"title\": \"Import Jaguar\",\n                \"description\": \"\",\n                \"id\": \"romimport-jaguar\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"25\",\n                \"title\": \"Import GameGear\",\n                \"description\": \"\",\n                \"id\": \"romimport-gg\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\"\n            },\n            {\n                \"index\": \"26\",\n                \"title\": \"Import NES/Famicom\",\n                \"description\": \"\",\n                \"id\": \"romimport-nes\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"27\",\n                \"title\": \"Import Atari 5200\",\n                \"description\": \"\",\n                \"id\": \"romimport-a5200\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"28\",\n                \"title\": \"Import PC Engine\",\n                \"description\": \"\",\n                \"id\": \"romimport-pce\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"29\",\n                \"title\": \"Import Wonderswan\",\n                \"description\": \"\",\n                \"id\": \"romimport-wonderswan\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"30\",\n                \"title\": \"Import Famicom Disk System\",\n                \"description\": \"\",\n                \"id\": \"romimport-fds\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\": \"romimport\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/romm_cifs.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"RomM\",\n        \"description\": \"Install the RomM Samba service\",\n        \"id\":\"romm-cifs\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install Samba for RomM support\",\n                \"id\":\"romm-cifs\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"romm_cifs\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Enable\",\n                \"description\": \"Enable auto dir updates\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_enable_start\",\n                \"group\":\"\",\n                \"command\": \"retronas-romm-dirs.timer\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Disable\",\n                \"description\": \"Disable auto dir updater\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_disable_stop\",\n                \"group\":\"\",\n                \"command\": \"retronas-romm-dirs.timer\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"Status\",\n                \"description\": \"Query auto dir updater\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_status\",\n                \"group\":\"\",\n                \"command\": \"retronas-romm-dirs*\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"06\",\n                \"title\": \"Run\",\n                \"description\": \"Run directory updater now\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start_follow\",\n                \"group\":\"\",\n                \"command\": \"retronas-romm-dirs.service\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"ROMM.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config/menu/samba.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"samba\",\n        \"description\": \"Install samba\",\n        \"id\":\"samba\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install samba\",\n                \"id\":\"samba\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"samba\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"smbd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"script-static\",\n                \"group\":\"\",\n                \"command\": \"service-samba\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"smbd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"Samba.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/seaweedfs.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"wrp\",\n        \"description\": \"Install wrp\",\n        \"id\":\"wrp\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install seaweedfs\",\n                \"id\":\"seaweedfs\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"seaweedfs\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"seaweedfs-retronas\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"seaweedfs-retronas\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"seaweedfs-retronas\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"23\",\n                \"title\": \"Access\",\n                \"description\": \"view access keys for the retronas identity\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"script\",\n                \"group\":\"\",\n                \"command\": \"seaweedfs-credentials\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"seaweedfs.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config/menu/services.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"Services\",\n        \"description\": \"Please select a service to check\",\n        \"id\":\"services\",\n        \"prompt\": \"Check\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/sslcert.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"SSL Certificate\",\n        \"description\": \"Manage the SSL Certificate\",\n        \"id\":\"sslcert\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Generate\",\n                \"description\": \"Generate a self-signed certificate\",\n                \"id\":\"sslcert\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"sslcert\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"SSL.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config/menu/syncthing.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"syncthing\",\n        \"description\": \"Install syncthing\",\n        \"id\":\"syncthing\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install syncthing\",\n                \"id\":\"syncthing\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"syncthing\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"syncthing\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"syncthing\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"syncthing\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"Syncthing.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/tcpser.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"tcpser\",\n        \"description\": \"Please select an option\",\n        \"id\":\"tcpser-menu\",\n        \"prompt\": \"\",\n        \"type\": \"modal\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install tcpser application\",\n                \"id\":\"tcpser\",\n                \"prompt\": \"Install\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"tcpser\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Serial Device\",\n                \"description\": \"Create/Edit Serial Device\",\n                \"id\":\"tcpser-edit-serial\",\n                \"prompt\": \"\",\n                \"type\": \"form\",\n                \"group\":\"\",\n                \"command\": \"tcpser_edit\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Virtual Device\",\n                \"description\": \"Create/Edit Virtual Device (VICE RS232)\",\n                \"id\":\"tcpser-edit-virtual\",\n                \"prompt\": \"\",\n                \"type\": \"form\",\n                \"group\":\"\",\n                \"command\": \"tcpser_edit\",\n                \"args\": \"VIRTUAL\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"Start\",\n                \"description\": \"Start tcpser device\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"form\",\n                \"group\":\"\",\n                \"command\": \"tcpser_status\",\n                \"args\": \"START\"\n            },\n            {\n                \"index\":\"06\",\n                \"title\": \"Status\",\n                \"description\": \"Query tcpser device\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"form\",\n                \"group\":\"\",\n                \"command\": \"tcpser_status\",\n                \"args\": \"STATUS\"\n            },\n            {\n                \"index\":\"07\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop tcpser device\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"form\",\n                \"group\":\"\",\n                \"command\": \"tcpser_status\",\n                \"args\": \"STOP\"\n            }\n        ]\n    },\n    \"tcpser-edit\": \n    {\n        \"title\": \"tcpser Modem Config\",\n        \"description\": \"Config path: \\\"${TCPSER_CONFIG_PATH}\\\"||Legend:|- VIRTUAL: VICE RS232 mode|- PHYSICAL: for USB/Real serial ports||Attention: A Device is considered unique by listen port. Configurations are not read in from existing. This port config will be overwritten on close.\",\n        \"id\":\"tcpser-edit-menu\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Edit\",\n                \"description\": \"Edit Device\",\n                \"id\":\"tcpser-edit-serial-input\",\n                \"prompt\": \"\",\n                \"type\": \"input:input\",\n                \"group\":\"tcpser\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Port\",\n                \"description\": \"Listen Port\",\n                \"id\":\"tcpser-edit-port-input\",\n                \"prompt\": \"\",\n                \"type\": \"input:input\",\n                \"group\":\"tcpser\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Speed\",\n                \"description\": \"Baud Rate\",\n                \"id\":\"tcpser-edit-speed-input\",\n                \"prompt\": \"\",\n                \"type\": \"input:input\",\n                \"group\":\"tcpser\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"Init String\",\n                \"description\": \"Hayes Modem Init String (without leading A)\",\n                \"id\":\"tcpser-edit-init-input\",\n                \"prompt\": \"\",\n                \"type\": \"input:input\",\n                \"group\":\"tcpser\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"06\",\n                \"title\": \"Additional\",\n                \"description\": \"Additional TCPser Options\",\n                \"id\":\"tcpser-edit-additional-input\",\n                \"prompt\": \"\",\n                \"type\": \"input:input\",\n                \"group\":\"tcpser\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"06\",\n                \"title\": \"Mode\",\n                \"description\": \"Device Mode\",\n                \"id\":\"tcpser-edit-mode-input\",\n                \"prompt\": \"\",\n                \"type\": \"input:dropdown:disabled\",\n                \"group\":\"tcpser\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            }\n        ]\n    },\n    \"tcpser-service\": \n    {\n        \"title\": \"tcpser\",\n        \"description\": \"Please select an option\",\n        \"id\":\"tcpser-menu\",\n        \"prompt\": \"\",\n        \"type\": \"modal\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\": \"01\",\n                \"title\": \"Listen Port\",\n                \"description\": \"\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"input:input\",\n                \"group\":\"\",\n                \"command\": \"\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"tcpser.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config/menu/telnet.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"telnet\",\n        \"description\": \"Install telnet\",\n        \"id\":\"telnet\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install telnet\",\n                \"id\":\"telnet\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"telnet\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"inetd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"inetd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"inetd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"Telnet.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/tftpd-hpa.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"tftpd-hpa\",\n        \"description\": \"Install tftpd-hpa\",\n        \"id\":\"tftpd-hpa\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install tftpd-hpa\",\n                \"id\":\"tftpd-hpa\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"tftpd-hpa\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"tftpd-hpa\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"tftpd-hpa\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"tftpd-hpa\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"tftp.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config/menu/tnfs.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"tnfsd\",\n        \"description\": \"Install tnfsd\",\n        \"id\":\"tnfsd\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install tnfsd\",\n                \"id\":\"tnfsd\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"tnfs\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"tnfsd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"tnfsd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"tnfsd\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"TNFS.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/tools.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"Tools\",\n        \"description\": \"Please select an option to install\",\n        \"id\":\"tools\",\n        \"prompt\": \"Run\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/update.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"Update\",\n        \"description\": \"Please select an option\",\n        \"id\":\"update\",\n        \"prompt\": \"Update\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\",\n                \"id\":\"\",\n                \"prompt\": \"\", \n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"RetroNAS\",\n                \"description\": \"Update RetroNAS\",\n                \"id\":\"update-retronas\",\n                \"prompt\": \"\",\n                \"type\": \"script-static\",\n                \"group\":\"\",\n                \"command\": \"update-retronas\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Rom Dir(s)\",\n                \"description\": \"Update core RetroNAS rom directories\",\n                \"id\":\"romdir\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"romdir\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Extra Dir(s)\",\n                \"description\": \"Update extra RetroNAS directories (bios, saves etc)\",\n                \"id\":\"extradirs\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"extradirs\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"GNU/Linux\",\n                \"description\": \"Update GNU/Linux Operating System packages\",\n                \"id\":\"update-system\",\n                \"prompt\": \"\",\n                \"type\": \"script-static\",\n                \"group\":\"\",\n                \"command\": \"update-system\"\n            },\n            {\n                \"index\":\"06\",\n                \"title\": \"History\",\n                \"description\": \"Update History for GNU/Linux Operating System packages\",\n                \"id\":\"update-system-history\",\n                \"prompt\": \"View\",\n                \"type\": \"script-static\",\n                \"group\":\"\",\n                \"command\": \"update-system-history\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"07\",\n                \"title\": \"Branch\",\n                \"description\": \"Manage the active GIT branch, (test new features, etc)\",\n                \"id\":\"git-switch-branch\",\n                \"prompt\": \"Manage\",\n                \"type\": \"script-static\",\n                \"group\":\"\",\n                \"command\": \"git-switch-branch\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"08\",\n                \"title\": \"Clean\",\n                \"description\": \"Clean up broken symlinks\",\n                \"id\":\"clean-broken-symlinks\",\n                \"prompt\": \"Run\",\n                \"type\": \"script-static\",\n                \"group\":\"\",\n                \"command\": \"clean-broken-symlinks\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config/menu/webone.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"webone\",\n        \"description\": \"Install webone\",\n        \"id\":\"webone\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install webone\",\n                \"id\":\"webone\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"webone\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"webone\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"webone\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"webone\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"WebOne.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/wrp.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"wrp\",\n        \"description\": \"Install wrp\",\n        \"id\":\"wrp\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install wrp\",\n                \"id\":\"wrp\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"wrp\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"wrp\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"wrp\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"wrp\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"wrp.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/xbox360_netiso.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"Xbox360 NetISO\",\n        \"description\": \"Install Xbox360 NetISO\",\n        \"id\":\"xbox360_netiso\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install xbox360_netiso\",\n                \"id\":\"xbox360_netiso\",\n                \"prompt\": \"\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"xbox360_netiso\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_enable_start\",\n                \"group\":\"\",\n                \"command\": \"xbox360_netiso\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_status\",\n                \"group\":\"\",\n                \"command\": \"xbox360_netiso\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_disable_stop\",\n                \"group\":\"\",\n                \"command\": \"xbox360_netiso\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"NetISO.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "config/menu/xlinkkai.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"XLink Kai\",\n        \"description\": \"XLink Kai online gaming service for LAN based game modes||WARNING: XLink Kai works by capturing and inspecting network traffic, we recommend you isolate devices running XLink from your main net\",\n        \"id\":\"xlinkkai-menu\",\n        \"prompt\": \"\",\n        \"type\": \"modal\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install the XLink Kai application\",\n                \"id\":\"xlink-kai\",\n                \"prompt\": \"Install\",\n                \"type\":\"install\",\n                \"group\":\"\",\n                \"command\": \"xlink-kai\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"20\",\n                \"title\": \"Start\",\n                \"description\": \"Start service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"xlink-kai\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"21\",\n                \"title\": \"Status\",\n                \"description\": \"Query service status\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\":\"service_status\",\n                \"group\":\"\",\n                \"command\": \"xlink-kai\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"22\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop the service\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"xlink-kai\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"XLink-Kai.md\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/menu/zterm.json",
    "content": "{\n    \"menu\": \n    {\n        \"title\": \"zterm\",\n        \"description\": \"Please select an option\",\n        \"id\":\"zterm-menu\",\n        \"prompt\": \"\",\n        \"type\": \"modal\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Install\",\n                \"description\": \"Install zterm application\",\n                \"id\":\"zterm\",\n                \"prompt\": \"Install\",\n                \"type\": \"install\",\n                \"group\":\"\",\n                \"command\": \"zterm\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Serial Device\",\n                \"description\": \"Create/Edit Serial Device\",\n                \"id\":\"zterm-edit-serial\",\n                \"prompt\": \"\",\n                \"type\": \"form\",\n                \"group\":\"\",\n                \"command\": \"zterm_edit\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"04\",\n                \"title\": \"Start\",\n                \"description\": \"Start zterm device\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_start\",\n                \"group\":\"\",\n                \"command\": \"zterm\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"05\",\n                \"title\": \"Stop\",\n                \"description\": \"Stop zterm device\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_stop\",\n                \"group\":\"\",\n                \"command\": \"zterm\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"06\",\n                \"title\": \"Status\",\n                \"description\": \"Query zterm device\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"service_status\",\n                \"group\":\"\",\n                \"command\": \"zterm\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"99\",\n                \"title\": \"Documentation\",\n                \"description\": \"Read the service documentation\",\n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"documentation\",\n                \"group\":\"\",\n                \"command\": \"zterm.md\",\n                \"args\": \"\"\n            }\n        ]\n    },\n    \"zterm-edit\": \n    {\n        \"title\": \"zterm Device Config\",\n        \"description\": \"set zterm device\",\n        \"id\":\"zterm-edit-menu\",\n        \"prompt\": \"\",\n        \"type\": \"menu\",\n        \"items\": [\n            {\n                \"index\": \"01\",\n                \"title\": \"Back\",\n                \"description\": \"Return to previous menu\", \n                \"id\":\"\",\n                \"prompt\": \"\",\n                \"type\": \"menu\",\n                \"group\":\"\",\n                \"command\": \"EXIT_OK\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"02\",\n                \"title\": \"Edit\",\n                \"description\": \"Edit Device\",\n                \"id\":\"zterm-edit-serial-input\",\n                \"prompt\": \"\",\n                \"type\": \"input:input\",\n                \"group\":\"zterm\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            },\n            {\n                \"index\":\"03\",\n                \"title\": \"Speed\",\n                \"description\": \"Baud Rate\",\n                \"id\":\"zterm-edit-speed-input\",\n                \"prompt\": \"\",\n                \"type\": \"input:input\",\n                \"group\":\"zterm\",\n                \"command\": \"TOOL COMMAND\",\n                \"args\": \"\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "config/retronas.cfg",
    "content": "#!/bin/bash\n\n# Dont allow unbound vars, this should be default eventually\nset -u\n\n# Set paths\nexport RNDIR=/opt/retronas\nexport RNDOC=${RNDIR}/doc\nexport USER_CONFIG=${RNDIR}/etc\nexport ANDIR=${RNDIR}/ansible\nexport ANCFG=${ANDIR}/retronas_vars.yml\nexport ANSIBLE_CONFIG=${ANDIR}/ansible.cfg\nexport ACACHEDIR=${RNDIR}/cache\nexport DIDIR=${RNDIR}/dialog\nexport SCDIR=${RNDIR}/scripts\nexport PCHDIR=${SCDIR}/patch/\nexport LANGDIR=${RNDIR}/lang\nexport LIBDIR=${RNDIR}/lib\nexport LOGDIR=${RNDIR}/log\nexport CONFIGDIR=${RNDIR}/config\nexport TDIR=/dev/shm/retronas\nexport RNLANG=\"en\"\nexport RNJSONOLD=${CONFIGDIR}/retronas.json\nexport RNJSON=${CONFIGDIR}/menu\n\n# migrate to using etc for user config\nexport OLDAGREEMENT=${RNDIR}/user_agreement\nexport AGREEMENT=${USER_CONFIG}/user_agreement\n\n[ -d \"${TDIR}\" ] && rm -rf \"${TDIR}\" 2>/dev/null\nmkdir -p \"${TDIR}\" 2>/dev/null\n\n# RetroNAS menu geometry\nexport MH=\"80\"\nexport MW=\"24\"\nexport MG=\"${MW} ${MH}\"\n\nif [ -f \"${ANCFG}\" ]\nthen\n  # Find the current config items\n  export OLDRNBRANCH=$(cd ${RNDIR};git branch | awk '/\\*/{print $2}')\n  # GOG Settings\n  export OLDGOGOS=\"$( awk -F': ' '/^retronas_gog_os:/{gsub(/\"/,\"\");print $2}' ${ANCFG} )\"\n  export OLDGOGLANG=\"$( awk -F': ' '/^retronas_gog_lang:/{gsub(/\"/,\"\");print $2}' ${ANCFG} )\"\n\n\tIFS=$'\\n'\n\tfor LINE in $(<${ANCFG})\n\tdo\n\t\tIFS=\": \" read -r -a PIECES <<< $LINE\n\t\tVALUE=${PIECES[1]//\\\"/}\n\t\tcase ${PIECES[0]} in\n\t\t  retronas_user)\n\t\t\t  OLDRNUSER=$VALUE\n\t\t\t  ;;\n\t\t  retronas_group)\n\t\t\t  OLDRNGROUP=$VALUE\n\t\t\t  ;;\n\t\t  retronas_path)\n\t\t\t  OLDRNPATH=$VALUE\n\t\t\t  ;;\n\t\t  retronas_net_3dsqr_interface)\n\t\t\t  OLD3DSIF=$VALUE\n\t\t\t  ;;\n\t\t  retronas_etherdfs_interface)\n\t\t\t  OLDETHERDFSIF=$VALUE\n\t\t\t  ;;\n\t\t  retronas_net_retro_interface)\n\t\t\t  OLDRETROIF=$VALUE\n\t\t\t  ;;\n\t\t  retronas_net_modern_interface)\n\t\t\t  OLDMODERNIF=$VALUE\n\t\t\t  ;;\n\t\t  retronas_net_retro_dhcprange)\n\t\t\t  OLDRETRODHCP=$VALUE\n\t\t\t  ;;\n\t\t  retronas_net_retro_router)\n\t\t\t  OLDRETROROUTER=$VALUE\n\t\t\t  ;;\n\t\t  retronas_net_retro_ntp)\n\t\t\t  OLDRETRONTP=$VALUE\n\t\t\t  ;;\n\t\t  retronas_net_retro_dns)\n\t\t\t  OLDRETRODNS=$VALUE\n\t\t\t  ;;\n\t\t  retronas_net_retro_ip)\n\t\t\t  OLDRETROIP=$VALUE\n\t\t\t  ;;\n\t\t  retronas_net_retro_subnet)\n\t\t\t  OLDRETROSUBNET=$VALUE\n\t\t\t  ;;\n\t\t  retronas_net_upstream_dns1)\n\t\t\t  OLDUPDNS1=$VALUE\n\t\t\t  ;;\n\t\t  retronas_net_upstream_dns2)\n\t\t\t  OLDUPDNS2=$VALUE\n\t\t\t  ;;\n\t\t  retronas_net_wifi_interface)\n\t\t\t  OLDWIFIINT=$VALUE\n\t\t\t  ;;\n\t\t  retronas_net_wifi_ssid)\n\t\t\t  OLDWIFISSID=$VALUE\n\t\t\t  ;;\n\t\t  retronas_net_wifi_channel)\n\t\t\t  OLDWIFICHANNEL=$VALUE\n\t\t\t  ;;\n\t\t  retronas_net_wifi_hwmode)\n\t\t\t  OLDWIFIHWMODE=$VALUE\n\t\t\t  ;;\n\t\t  retronas_net_wifi_countrycode)\n\t\t\t  OLDWIFICC=$VALUE\n\t\t\t  ;;\n\t\t  retronas_net_wifi_ip)\n\t\t\t  OLDWIFIIP=$VALUE\n\t\t\t  ;;\n\t\t  retronas_net_wifi_subnet)\n\t\t\t  OLDWIFISUBNET=$VALUE\n\t\t\t  ;;\n\t\t  retronas_net_wifi_dhcprange)\n\t\t\t  OLDWIFIDHCP=$VALUE\n\t\t\t  ;;\n\t\t  retronas_net_wifi_router)\n\t\t\t  OLDWIFIROUTER=$VALUE\n\t\t\t  ;;\n\t\t  retronas_net_wifi_ntp)\n\t\t\t  OLDWIFINTP=$VALUE\n\t\t\t  ;;\n\t\t  retronas_net_wifi_dns)\n\t\t\t  OLDWIFIDNS=$VALUE\n\t\t\t  ;;\n\t\tesac\n\tdone\nfi\n\n# Find IPs\n#export MY_IPS=$( ip a | grep 'inet ' | awk '/inet/{print $2}' | awk -F '/' '{print $1}' | grep -v ^127  )\nexport MY_IPV4=$( ip -4 -br a | awk '!/127/{sub(/\\/[0-9].+$/, \"\"); print $3}' )\nexport MY_IPV6=$( ip -6 -br a | awk '!/::1/{sub(/\\/[0-9].+$/, \"\"); print $3}' )\nexport MY_IPS=( $MY_IPV4 $MY_IPV6 )\n\n\n# Set some messages\nexport APPNAME=\"RetroNAS\"\nexport PAUSEMSG='Press [Enter] to continue...'\nexport IPADDMSG=\"IP: ${MY_IPS}\"\n\n\n# reset this at the end here so it doesn't screw existing scripts\nset +u\n"
  },
  {
    "path": "dialog/adtpro_ethernet_edit.sh",
    "content": "#!/bin/bash\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\nMENU_NAME=adtpro_ethernet_edit\ncd ${DIDIR}\n\nOUTDIR=/opt/adtpro\nSERVICE_CONFIG=${OUTDIR}/ADTPro.properties\n\n[ ! -f ${SERVICE_CONFIG} ] && echo \"Install adtpro first\" && PAUSE\n\n# DEFAULTS\nPORT=\"$(awk -F \"=\" '/^UDPServerPort/{print $2}' ${SERVICE_CONFIG})\"\n\nsource $_CONFIG\ncd ${DIDIR}\n\nrn_adtpro_ethernet_edit() {\n  READ_MENU_TDESC \"${MENU_NAME}\"\n\n  MENU_ARRAY=(\n    \"Listen Port:\"            1 5 \"$PORT\"     1 20 20 20\n  )\n\n  DLG_FORM \"${MENU_TNAME}\" \"${MENU_ARRAY}\" 8 \"${MENU_BLURB}\"\n\n  [ ${#CHOICE[@]}  -gt 0 ] && rn_adtpro_write_config\n\n}\n\nrn_adtpro_write_config() {\n\n  CLEAR\n  if [ ! -z ${CHOICE[0]} ] \n  then\n    echo \"Updating device to ${DEVSTR}${CHOICE[0]}\"\n    sed -i -r \"s#^UDPServerPort.+#UDPServerPort=${DEVSTR}${CHOICE[0]}#\" $SERVICE_CONFIG\n\n    #sed -i -r \"s#^Condition.+#ConditionPathExists=${DEVSTR}${CHOICE[0]}#\" /usr/lib/systemd/system/adtpro.service\n    systemctl daemon-reload\n\n    echo \"Config file updated, now you can (re)start the service from the Ethernet menu\"\n    PAUSE\n  else\n    echo \"No change\" \n    PAUSE\n  fi\n  EXIT_OK\n}\n\nrn_adtpro_ethernet_edit\n"
  },
  {
    "path": "dialog/adtpro_localhost_edit.sh",
    "content": "#!/bin/bash\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\nMENU_NAME=adtpro_serial_edit\ncd ${DIDIR}\n\nOUTDIR=/opt/adtpro\nSERVICE_CONFIG=${OUTDIR}/ADTPro.properties\n\n[ ! -f ${SERVICE_CONFIG} ] && echo \"Install adtpro first\" && PAUSE\n\n# DEFAULTS\nSIPHOST=\"$(awk -F \"=\" '/^SerialIPHost\\=.+$/{print $2}' ${SERVICE_CONFIG})\"\nSIPPORT=\"$(awk -F \"=\" '/^SerialIPPort/{print $2}' ${SERVICE_CONFIG})\"\n\nsource $_CONFIG\ncd ${DIDIR}\n\nrn_adtpro_serial_edit() {\n  READ_MENU_TDESC \"${MENU_NAME}\"\n\n  MENU_ARRAY=(\n    \"Remote Host:\"       1 5 \"$SIPHOST\"     1 20 20 20\n    \"Remote Port:\"       2 5 \"$SIPPORT\"     2 20 20 20\n  )\n\n  DLG_FORM \"${MENU_TNAME}\" \"${MENU_ARRAY}\" 8 \"${MENU_BLURB}\"\n\n  [ ${#CHOICE[@]}  -gt 1 ] && rn_adtpro_write_config\n\n}\n\nrn_adtpro_write_config() {\n\n  CLEAR\n  if [ ! -z ${CHOICE[0]} ] \n  then\n    echo \"Updating device to ${DEVSTR}${CHOICE[0]}\"\n    sed -i -r \"s#^SerialIPHost\\=.+#SerialIPHost=${DEVSTR}${CHOICE[0]}#\" ${SERVICE_CONFIG}\n    sed -i -r \"s#^SerialIPPort.+#SerialIPPort=${DEVSTR}${CHOICE[1]}#\" ${SERVICE_CONFIG}\n\n    #sed -i -r \"s#^Condition.+#ConditionPathExists=${DEVSTR}${CHOICE[0]}#\" /usr/lib/systemd/system/adtpro.service\n    systemctl daemon-reload\n\n    echo \"Config file updated, now you can (re)start the service from the Serial menu\"\n    PAUSE\n  else\n    echo \"No change\" \n    PAUSE\n  fi\n  EXIT_OK\n}\n\nrn_adtpro_serial_edit\n"
  },
  {
    "path": "dialog/adtpro_serial_edit.sh",
    "content": "#!/bin/bash\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\nMENU_NAME=adtpro_serial_edit\ncd ${DIDIR}\n\nOUTDIR=/opt/adtpro\nSERVICE_CONFIG=${OUTDIR}/ADTPro.properties\n\n[ ! -f ${SERVICE_CONFIG} ] && echo \"Install adtpro first\" && PAUSE\n\n# DEFAULTS\nDEVICE=\"$(awk -F \"=\" '/^CommPort\\=.+$/{print $2}' ${SERVICE_CONFIG})\"\nSPEED=\"$(awk -F \"=\" '/^CommPortSpeed/{print $2}' ${SERVICE_CONFIG})\"\nBSPEED=\"$(awk -F \"=\" '/^CommPortBootstrapSpeed/{print $2}' ${SERVICE_CONFIG})\"\nBPACING=\"$(awk -F \"=\" '/^CommPortBootstrapPacing/{print $2}' ${SERVICE_CONFIG})\"\nHANDSHAKE=\"$(awk -F \"=\" '/^HardwareHandshaking/{print $2}' ${SERVICE_CONFIG})\"\n\n\nsource $_CONFIG\ncd ${DIDIR}\n\nrn_adtpro_serial_edit() {\n  READ_MENU_TDESC \"${MENU_NAME}\"\n\n  MENU_ARRAY=(\n    \"Comm Port Device:\"       1 5 \"$DEVICE\"     1 27 20 20\n    \"Comm Port Speed:\"        2 5 \"$SPEED\"      2 27 20 20\n    \"Bootstrap Speed:\"        3 5 \"$BSPEED\"     3 27 20 20\n    \"Bootstrap Pacing:\"       4 5 \"$BPACING\"    4 27 20 20\n    \"Hardware Handshaking:\"   5 5 \"$HANDSHAKE\"  5 27 20 20\n  )\n\n  DLG_FORM \"${MENU_TNAME}\" \"${MENU_ARRAY}\" 8 \"${MENU_BLURB}\"\n\n  [ ${#CHOICE[@]}  -gt 1 ] && rn_adtpro_write_config\n\n}\n\nrn_adtpro_write_config() {\n\n  CLEAR\n  if [ ! -z ${CHOICE[0]} ] \n  then\n    echo \"Updating device to ${DEVSTR}${CHOICE[0]}\"\n    sed -i -r \"s#^CommPort\\=.+#CommPort=${DEVSTR}${CHOICE[0]}#\" ${SERVICE_CONFIG}\n    sed -i -r \"s#^CommPortSpeed.+#CommPortSpeed=${DEVSTR}${CHOICE[1]}#\" ${SERVICE_CONFIG}\n    sed -i -r \"s#^CommPortBootstrapSpeed.*#CommPortBootstrapSpeed=${DEVSTR}${CHOICE[2]}#\" ${SERVICE_CONFIG}\n    sed -i -r \"s#^CommPortBootstrapPacing.*#CommPortBootstrapPacing=${DEVSTR}${CHOICE[3]}#\" ${SERVICE_CONFIG}\n    sed -i -r \"s#^HardwareHandshaking.*#HardwareHandshaking=${DEVSTR}${CHOICE[4]}#\" ${SERVICE_CONFIG}\n\n    #sed -i -r \"s#^Condition.+#ConditionPathExists=${DEVSTR}${CHOICE[0]}#\" /usr/lib/systemd/system/adtpro.service\n    systemctl daemon-reload\n\n    echo \"Config file updated, now you can (re)start the service from the Serial menu\"\n    PAUSE\n  else\n    echo \"No change\" \n    PAUSE\n  fi\n  EXIT_OK\n}\n\nrn_adtpro_serial_edit\n"
  },
  {
    "path": "dialog/d_input.sh",
    "content": "_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\ncd ${DIDIR}\nMENU_NAME=$1\nCLEAR\n\n### this needs to be smarter oneday\ncase $MENU_NAME in\n    set-retronas-net-3dsqr-interface)\n        OLDVALUE=${OLD3DSIF}\n        DATASET=($(ip link show | awk '/^[0-9]/{gsub(/:/,\"\");print $2}'))\n        PATTERN=\"retronas_net_3dsqr_interface:\"\n        ;;\n    set-etherdfs-nic)\n        OLDVALUE=${OLDETHERDFSIF}\n        DATASET=($(ip link show | awk '/^[0-9]/{gsub(/:/,\"\");print $2}'))\n        PATTERN=\"retronas_etherdfs_interface:\"\n        ;;\n    set-top-level-dir)\n        OLDVALUE=\"${OLDRNPATH}\"\n        DATASET=()\n        PATTERN=\"retronas_path:\"\n        ;;\n    update-user)\n        OLDVALUE=\"${OLDRNUSER}\"\n        DATASET=($(awk -F':' '{print $1}' /etc/passwd  | paste -d\" \" -s))\n        PATTERN=\"retronas_user:\"\n        ;;\n    update-group)\n        OLDVALUE=\"${OLDRNGROUP}\"\n        DATASET=($(awk -F':' '{print $1}' /etc/group  | paste -d\" \" -s))\n        PATTERN=\"retronas_group:\"\n        ;;\n    set-retronas-net-modern-nic)\n        OLDVALUE=${OLDMODERNIF}\n        DATASET=($(ip link show | awk '/^[0-9]/{gsub(/:/,\"\");print $2}'))\n        PATTERN=\"retronas_net_modern_interface:\"\n        ;;\n    set-retronas-net-retro-nic)\n        OLDVALUE=${OLDRETROIF}\n        DATASET=($(ip link show | awk '/^[0-9]/{gsub(/:/,\"\");print $2}'))\n        PATTERN=\"retronas_net_retro_interface:\"\n        ;;\n    set-retronas-net-retro-dns)\n        OLDVALUE=${OLDRETRODNS}\n        DATASET=()\n        PATTERN=\"retronas_net_retro_dns:\"\n        ;;\n    set-retronas-net-retro-dhcprange)\n        OLDVALUE=${OLDRETRODHCP}\n        DATASET=()\n        PATTERN=\"retronas_net_retro_dhcprange:\"\n        ;;\n    set-retronas-net-retro-router)\n        OLDVALUE=${OLDRETROROUTER}\n        DATASET=()\n        PATTERN=\"retronas_net_retro_router:\"\n        ;;\n    set-retronas-net-retro-ntp)\n        OLDVALUE=${OLDRETRONTP}\n        DATASET=()\n        PATTERN=\"retronas_net_retro_ntp:\"\n        ;;\n    set-retronas-net-retro-ip)\n        OLDVALUE=${OLDRETROIP}\n        DATASET=()\n        PATTERN=\"retronas_net_retro_ip:\"\n        ;;\n    set-retronas-net-retro-subnet)\n        OLDVALUE=${OLDRETROSUBNET}\n        DATASET=()\n        PATTERN=\"retronas_net_retro_subnet:\"\n        ;;\n    set-retronas-net-upstream-dns1)\n        OLDVALUE=${OLDUPDNS1}\n        DATASET=()\n        PATTERN=\"retronas_net_upstream_dns1:\"\n        ;;\n    set-retronas-net-upstream-dns2)\n        OLDVALUE=${OLDUPDNS2}\n        DATASET=()\n        PATTERN=\"retronas_net_upstream_dns2:\"\n        ;;\n    set-retronas-net-wifi-countrycode)\n        OLDVALUE=${OLDWIFICC}\n        DATASET=(AE AR AT AU BG BH BM BO BR CA CH CL CN CO CR CS CY CZ DE DK DO DZ EC EE EG ES FI FR GB GR GT HK HN ID IE IL IN IS IT JM JO JP3 KE KR KW KW LB LI LI LK LT LT LU MA MA MU MX MX NL NO NZ NZ OM PA PA PE PH PK PL PL PR PR PT QA RO RU RU SA SG SI SI SK SK SV TH TH TN TR TT TW UA US UY UY VE VN ZA)\n        PATTERN=\"retronas_net_wifi_countrycode:\"\n        ;;\n    set-retronas-net-wifi-interface)\n        OLDVALUE=${OLDWIFIINT}\n        DATASET=()\n        PATTERN=\"retronas_net_wifi_interface:\"\n        ;;\n    set-retronas-net-wifi-ssid)\n        OLDVALUE=${OLDWIFISSID}\n        DATASET=()\n        PATTERN=\"retronas_net_wifi_ssid:\"\n        ;;\n    set-retronas-net-wifi-channel)\n        OLDVALUE=${OLDWIFICHANNEL}\n        DATASET=()\n        PATTERN=\"retronas_net_wifi_channel:\"\n        ;;\n    set-retronas-net-wifi-hwmode)\n        OLDVALUE=${OLDWIFIHWMODE}\n        DATASET=(a bg)\n        PATTERN=\"retronas_net_wifi_hwmode:\"\n        ;;\n    set-retronas-net-wifi-ip)\n        OLDVALUE=${OLDWIFIIP}\n        DATASET=()\n        PATTERN=\"retronas_net_wifi_ip:\"\n        ;;\n    set-retronas-net-wifi-subnet)\n        OLDVALUE=${OLDWIFISUBNET}\n        DATASET=()\n        PATTERN=\"retronas_net_wifi_subnet:\"\n        ;;\n    set-retronas-net-wifi-dhcprange)\n        OLDVALUE=${OLDWIFIDHCP}\n        DATASET=()\n        PATTERN=\"retronas_net_wifi_dhcprange:\"\n        ;;\n    set-retronas-net-wifi-router)\n        OLDVALUE=${OLDWIFIROUTER}\n        DATASET=()\n        PATTERN=\"retronas_net_wifi_router:\"\n        ;;\n    set-retronas-net-wifi-ntp)\n        OLDVALUE=${OLDWIFINTP}\n        DATASET=()\n        PATTERN=\"retronas_net_wifi_ntp:\"\n        ;;\n    set-retronas-net-wifi-dns)\n        OLDVALUE=${OLDWIFIDNS}\n        DATASET=()\n        PATTERN=\"retronas_net_wifi_dns:\"\n        ;;\n    *)\n        echo \"Menu not supported\"\n        PAUSE\n        EXIT_CANCEL\n        ;;\nesac\n\nrn_input() {\n    local MENU_NAME=\"${1}\"\n    READ_MENU_TDESC \"${MENU_NAME}\"\n    DLG_INPUTBOX \"${MENU_TNAME}\" \"${MENU_BLURB}\" \"${OLDVALUE}\"\n    export NEWVALUE=\"${CHOICE}\"\n\n    if [ ! -z $NEWVALUE ]\n    then\n        COMPARE_VALUES \"${NEWVALUE}\" \"${OLDVALUE}\"\n        if [ $CONFIRM -eq 1 ] \n        then\n            rn_input_confirm\n        else\n            rn_input $MENU_NAME\n        fi\n    fi\n}\n\n\nrn_input_confirm() {\n    unset CHOICE\n    # hacky fix for menu fix\n    [ $MENU_NAME == \"update-user\" ] && OLDRNUSER=$NEWVALUE\n    [ $MENU_NAME == \"update-group\" ] && OLDRNGROUP=$NEWVALUE\n\n    local MENU_PARENT=${MENU_NAME}\n    local MENU_NAME=\"${MENU_NAME}-confirm\"\n    READ_MENU_TDESC \"${MENU_NAME}\"\n    DLG_YN \"${MENU_TNAME}\" \"${MENU_BLURB}\"\n\n    case ${CHOICE} in\n        0)\n            # this is crap, fix it later - mostly just for directories atm\n            [ ${#DATASET[@]} -eq 0 ] && DATASET=($(ls -lad \"${NEWVALUE}\" 2>/dev/null | awk '{print $9}'))\n\n            echo ${DATASET[*]} | grep -qF ${NEWVALUE}\n            if [ $? -eq 0 ] || [ ${#DATASET[@]} -eq 0 ]\n            then\n                CLEAR\n                source $_CONFIG\n                sed -i \"/${PATTERN}/d\" \"${ANCFG}\"\n                echo \"${PATTERN} \\\"${NEWVALUE}\\\"\" >> \"${ANCFG}\"\n                source $_CONFIG\n                EXIT_OK\n            else\n                CLEAR\n                RN_LOG \"${NEWVALUE} is not a valid choice, please confirm your choice\"\n                PAUSE\n                rn_input $MENU_PARENT\n            fi\n            ;;\n        *)\n            exit ${CHOICE}\n            ;;\n    esac\n}\n\nrn_input $MENU_NAME"
  },
  {
    "path": "dialog/d_menu.sh",
    "content": "#!/bin/bash\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\nMENU_NAME=$1\ncd ${DIDIR}\nCHOICE=\"\"\n\nrn_menu() {\n  \n  source $_CONFIG\n  READ_MENU_JSON \"${MENU_NAME}\"\n  READ_MENU_TDESC \"${MENU_NAME}\"\n  DLG_MENUJ \"${MENU_TNAME}\" 10 \"${MENU_BLURB}\"\n\n}\n\nCLEAR\n\nwhile true\ndo\n  rn_menu\n  if [ ! -z \"${CHOICE}\" ]\n  then\n    READ_MENU_COMMAND ${MENU_NAME} ${CHOICE} \n  else\n    EXIT_CANCEL\n  fi\ndone\n"
  },
  {
    "path": "dialog/d_yn.sh",
    "content": "#!/bin/bash\n\nclear\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\nMENU_NAME=$1\ncd ${DIDIR}\n\nCLEAR\n\nrn_dialog_yn() {\n  source $_CONFIG\n\n  READ_MENU_JSON \"${MENU_NAME}\"\n  READ_MENU_TDESC \"${MENU_NAME}\"\n  DLG_YN \"${MENU_TNAME}\" \"${MENU_BLURB}\"\n\n  if [ ! -z \"${CHOICE}\" ]\n  then\n    READ_MENU_COMMAND ${MENU_NAME} ${CHOICE} \n  else\n    EXIT_CANCEL\n  fi\n\n}\n\nrn_dialog_yn ${MENU_NAME}\n"
  },
  {
    "path": "dialog/dexdrive_memcards.sh",
    "content": "#!/bin/bash\n\n#set -x\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\ncd ${DIDIR}\n\nCLEAR\n\nDEXEXT=\"*.mc*\"\nDEXPATH=/data/retronas/saves/sony/playstation1\nDEXDEV=/dev/dexdrive0\n\nIFS=\";\"\n\ncase $1 in\n  N64)\n    DEXEXT=\"*.n64\"\n    DEXPATH=/data/retronas/saves/nintendo/nintendo64\n    echo \"N64 is untested, let me know what raw save format is for N64\"\n    PAUSE\n    exit 0\n    ;;\nesac\n\nrn_dexdrive_game() {\n  local MENU_NAME=dexdrive_gamechooser\n  READ_MENU_JSON \"${MENU_NAME}\"\n  READ_MENU_TDESC \"${MENU_NAME}\"\n\n  MENU_ARRAY=()\n  MENU_ARRAY2=()\n\n  MENU_ARRAY2+=(\"${DEXPATH}\"/${DEXEXT})\n\n  if [ \"${#MENU_ARRAY2[@]}\" -le 0 ] || [ $(echo ${MENU_ARRAY2[@]} | grep \"*\" ) ]\n  then\n    echo \"No memcards found\"\n    PAUSE\n    exit 1\n  fi\n\n  IFS=\";\"\n  for ITEM in \"${MENU_ARRAY2[@]}\"\n  do\n    UPDATED=$(stat -c %y \"${ITEM}\" | awk '{print $1}')\n    ITEM2=\"${ITEM##*/}\"\n    MEMCARD=\"${ITEM2%%.*}\"\n    TYPE=${ITEM2##*.}\n    MENU_ARRAY+=\"$ITEM2;$UPDATED;\"\n  done\n\n  echo ${MENU_ARRAY[@]}\n  \n  dialog \\\n    --backtitle \"${MENU_TNAME}\" \\\n    --title \"${MENU_TNAME}\" \\\n    --clear \\\n    --menu \"${MENU_BLURB}\" ${MW} ${MH} 10 \\\n    ${MENU_ARRAY[@]} \\\n    2> ${TDIR}/rn_dexdrive\n \n  [ $? -eq 0 ] && rn_dexdrive_image\n\n}\n\nrn_dexdrive_image() {\n  CLEAR\n  NMEMCARD=\"$(cat ${TDIR}/rn_dexdrive)\"\n  echo \"Going to write \\\"${NMEMCARD}\\\" to ${DEXDEV}\"\n  if [ ! -z \"${NMEMCARD}\" ]\n  then\n    if [ -b ${DEXDEV} ]\n    then\n      echo \"Writing ${NMEMCARD} to ${DEXDEV}, please wait ...\"\n      dd if=\"${DEXPATH}/${NMEMCARD}\" of=${DEXDEV}\n      PAUSE\n    else\n      echo \"No Dex Drive found at ${DEXDEV}\"\n      PAUSE\n      exit 1\n    fi\n  else\n    exit 1\n  fi\n  PAUSE\n\n}\n\nrn_dexdrive_game\n"
  },
  {
    "path": "dialog/dexdrive_serial_edit.sh",
    "content": "#!/bin/bash\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\nMENU_NAME=dexdrive_serial_edit\ncd ${DIDIR}\n\n# DEFAULTS\nDEVICE=\"/dev/ttyUSB0\"\n\nsource $_CONFIG\ncd ${DIDIR}\n\nrn_dexdrive_serial_edit() {\n\n  READ_MENU_TDESC \"${MENU_NAME}\"\n\n  MENU_ARRAY=(\n    \"Serial Device:\"       1 5 \"$DEVICE\"     1 27 20 20\n  )\n\n  DLG_FORM \"${MENU_TNAME}\" \"${MENU_ARRAY}\" 8 \"${MENU_BLURB}\"\n\n  if [ ${#CHOICE[@]} -gt 0 ] \n  then \n      sed -r \"s#^(.*)\\s(.*)\\s(.*)#\\1 \\2 ${CHOICE[0]}#\" -i /etc/systemd/system/linux-dexdrive.service\n      systemctl daemon-reload\n  fi\n  EXIT_OK\n\n}\n\nrn_dexdrive_serial_edit\n"
  },
  {
    "path": "dialog/disclaimer.sh",
    "content": "#!/bin/bash\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\n# MIGRATION\n[ ! -d $(dirname $AGREEMENT) ] && mkdir $(dirname $AGREEMENT)\n[ -f $OLDAGREEMENT ] && mv $OLDAGREEMENT $AGREEMENT\n\n# User has already agreed, thanks all\nif [ -f $AGREEMENT ]\nthen\n  EXIT_OK\nfi\nclear\n\n## Check our information is available\nREQFAIL=0\n[ ! -f $RNDIR/SECURITY ] && echo \"SECURITY information is missing, please reinstall\" REQFAIL=1\n[ ! -f $RNDIR/LICENSE ] && echo \"LICENSE information was not found\" && REQFAIL=1\n[ $REQFAIL -ne 0 ] && \"Required information could not be found, see previous errors\" && exit $REQFAIL\n\ncat $RNDIR/SECURITY\necho -e \"\\n\"\nread -p \"LICENSE will follow, press Enter\"\necho \"\"\ncat $RNDIR/LICENSE\necho \"\"\nread -p \"type AGREE to accept the above in use of this project: \" INPUT\n\ncase $INPUT in\n  \"agree\"|\"AGREE\")\n    touch $AGREEMENT\n    EXIT_OK\n    ;;\n  *)\n    EXIT_CANCEL\nesac"
  },
  {
    "path": "dialog/gogrepo.sh",
    "content": "#!/bin/bash\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\ncd ${DIDIR}\n\n## Manifests and cookies stored in ~/.gogrepo\nCLEAR\n\n# dumb workaround\nchmod 755 ${SCDIR}\n\n\nrn_gog_chooser() {\n\n  local MENU_NAME=gogrepo\n  READ_MENU_JSON \"${MENU_NAME}\"\n  READ_MENU_TDESC \"${MENU_NAME}\"\n  DLG_MENUJ \"${MENU_TNAME}\" 10 \"${MENU_BLURB}\"\n  \n  while true\n  do\n    source $_CONFIG\n    \n    case ${CHOICE} in\n    01)\n      EXIT_OK\n      ;;\n    02)\n      READ_MENU_COMMAND ${MENU_NAME} ${CHOICE} \n      ;;\n    03)\n      # auth menu\n      rn_gog_auth\n      ;;\n    04)\n      # OS\n      rn_gog_setos\n      ;;\n    05)\n      # LANG\n      rn_gog_setlang\n      ;;\n    06)\n      # sync games list\n      CLEAR\n      DROP_ROOT ${SCDIR}/gogrepo_update.sh -skipknown -os ${OLDGOGOS} -lang ${OLDGOGLANG}\n      PAUSE\n      rn_gog_chooser\n      ;;\n    07)\n      # download 1 game\n      rn_gog_gameslist\n      rn_gog_game\n      ;;\n    08)\n      # download all games\n      CLEAR\n      DROP_ROOT ${SCDIR}/gogrepo_download.sh\n      PAUSE\n      ;;\n    09)\n      # sync and download\n      CLEAR\n      DROP_ROOT ${SCDIR}/gogrepo_update.sh -os ${OLDGOGOS} -lang ${OLDGOGLANG}\n      DROP_ROOT ${SCDIR}/gogrepo_download.sh\n      PAUSE\n      rn_gog_chooser\n      ;;\n    *)\n      EXIT_CANCEL\n      ;;\n    esac\n    unset CHOICE\n  done\n\n}\n\n\nrn_gog_setlang() {\n  source $_CONFIG\n\n  local MENU_NAME=gogrepo\n  READ_MENU_JSON \"${MENU_NAME}\" \"${MENU_NAME}-setuplang\"\n  READ_MENU_TDESC \"${MENU_NAME}\" \"${MENU_NAME}-setuplang\"\n  DLG_MENUJ \"${MENU_TNAME}\" 10 \"${MENU_BLURB}\"\n\n  case ${CHOICE} in\n    01)\n      rn_gog_chooser\n      ;;\n    ${CHOICE} )\n      NEWLANG=\"${CHOICE}\"\n      sed -i '/retronas_gog_lang:/d' \"${ANCFG}\"\n      echo \"retronas_gog_lang: \\\"${NEWLANG}\\\"\" >> \"${ANCFG}\"\n      ;;\n    *)\n      rn_gog_chooser\n      ;;\n    esac\n\n    rn_gog_chooser\n\n}\n\nrn_gog_auth() {\n  source $_CONFIG\n\n  local MENU_NAME=gogrepo\n  READ_MENU_JSON \"${MENU_NAME}\" \"${MENU_NAME}-auth\"\n  READ_MENU_TDESC \"${MENU_NAME}\" \"${MENU_NAME}-auth\"\n  DLG_MENUJ \"${MENU_TNAME}\" 10 \"${MENU_BLURB}\"\n\n  case ${CHOICE} in\n    02)\n      # login\n      CLEAR\n      DROP_ROOT ${SCDIR}/gogrepo_login.sh\n      PAUSE\n      ;;\n    03)\n      # import cookies\n      CLEAR\n      bash ${SCDIR}/gogrepo_import-cookies.sh\n      PAUSE\n      ;;\n    *)\n      rn_gog_chooser\n      ;;\n  esac\n\n}\n\nrn_gog_setos() {\n  source $_CONFIG\n\n  local MENU_NAME=gogrepo\n  READ_MENU_JSON \"${MENU_NAME}\" \"${MENU_NAME}-setupos\"\n  READ_MENU_TDESC \"${MENU_NAME}\" \"${MENU_NAME}-setupos\"\n  DLG_MENUJ \"${MENU_TNAME}\" 10 \"${MENU_BLURB}\"\n\n  case ${CHOICE} in\n    02)\n      NEWOS=\"windows\"\n      ;;\n    03)\n      NEWOS=\"mac\"\n      ;;\n    04)\n      NEWOS=\"linux\"\n      ;;\n    05)\n      NEWOS=\"windows mac\"\n      ;;\n    06)\n      NEWOS=\"windows linux\"\n      ;;\n    07)\n      NEWOS=\"mac linux\"\n      ;;\n    08)\n      NEWOS=\"windows mac linux\"\n      ;;\n    *)\n      rn_gog_chooser\n      ;;\n    esac\n\n    if [ ! -z $NEWOS ]; then\n      sed -i '/retronas_gog_os:/d' \"${ANCFG}\"\n      echo \"retronas_gog_os: \\\"${NEWOS}\\\"\" >> \"${ANCFG}\"\n    fi\n\n    rn_gog_chooser\n  \n}\n\nrn_gog_gameslist() {\n  rm ${TDIR}/rn_gog_gameslist 2>/dev/null\n  RNUDIR=$( getent passwd | grep ^${OLDRNUSER}\\: | awk -F ':' '{print $6}' )\n  if [ ! -f ${RNUDIR}/.gogrepo/gog-manifest.dat ]\n  then\n    echo \"GOG Manifest not found, you'll need to login/sync first\"\n    PAUSE\n    echo 1\n  else\n    grep \\'title\\'\\: ${RNUDIR}/.gogrepo/gog-manifest.dat | awk -F \\' '{print $4}' | sort | while read RN_GOG_ID\n    do\n      echo \"${RN_GOG_ID}\" >>${TDIR}/rn_gog_gameslist\n    done\n  fi\n}\n\nrn_gog_game() {\n  local MENU_NAME=gogrepo\n  READ_MENU_JSON \"${MENU_NAME}\" \"${MENU_NAME}-gamechooser\"\n  READ_MENU_TDESC \"${MENU_NAME}\" \"${MENU_NAME}-gamechooser\"\n\n  declare -a MENU_ARRAY\n  mapfile -t PIECES < ${TDIR}/rn_gog_gameslist\n  MENU_ARRAY+=(\"Back\" \".\")\n  for PIECE in ${PIECES[@]}\n  do      \n    MENU_ARRAY+=($PIECE \".\")\n  done\n\n  DLG_MENU \"${MENU_TNAME}\" \"${MENU_ARRAY}\" 10 \"${MENU_BLURB}\"\n\n  case ${CHOICE} in\n    Back)\n      rn_gog_chooser\n      ;;\n    ${CHOICE})\n      CLEAR\n      DROP_ROOT ${SCDIR}/gogrepo_update.sh -os ${OLDGOGOS} -id ${CHOICE} -lang ${OLDGOGLANG}\n      DROP_ROOT ${SCDIR}/gogrepo_download.sh -id ${CHOICE}\n      PAUSE\n      unset CHOICE\n      rn_gog_game\n      ;;\n    *)\n      rn_gog_chooser\n      ;;\n  esac\n\n  unset MENU_ARRAY\n}\n\nrn_gog_chooser\n"
  },
  {
    "path": "dialog/profiles.sh",
    "content": "#!/bin/bash\n\n#set -x\n\nclear\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\nPROFILED=\"${OLDRNPATH}/config\"\n\ncd ${DIDIR}\nCLEAR\n\nif [ ! -d \"${PROFILED}\" ]\nthen\n    echo \"${PROFILED} not found, exiting\"\n    PAUSE\n    exit 1\nfi\n\nrn_profile_chooser() {\n  local MENU_NAME=profiles\n  READ_MENU_JSON \"${MENU_NAME}\"\n  READ_MENU_TDESC \"${MENU_NAME}\"\n\n  MENU_ARRAY=()\n  MENU_ARRAY2+=(\"$PROFILED\"/*.ini )\n\n  if [ \"${#MENU_ARRAY2[@]}\" -le 0 ] || [ $(echo ${MENU_ARRAY2[@]} | grep \"*\" ) ]\n  then\n    echo \"No profiles found\"\n    PAUSE\n    exit 1\n  fi\n\n  IFS=\";\"\n  for ITEM in \"${MENU_ARRAY2[@]}\"\n  do\n\n    #TITLE=\"$(grep -E \"^title\\s=\" \"${ITEM}\" | cut -d' ' -f3-)\"\n    UPDATED=$(grep -E \"^last_updated\\s=\" \"${ITEM}\" | cut -d' ' -f3-)\n\n    [ -z \"${TITLE}\" ] && TITLE=\"Unknown\"\n    [ -z \"${UPDATED}\" ] && UPDATED=\"00-00-0000\"\n\n    ITEM2=${ITEM##*/}\n    ININAME=\"${ITEM2%%.*}\"\n    MENU_ARRAY+=\"$ININAME;$UPDATED;\"\n  done\n\n  dialog \\\n    --backtitle \"${MENU_NAME}\" \\\n    --title \"${MENU_NAME}\" \\\n    --clear \\\n    --menu \"${MENU_BLURB}\" ${MW} ${MH} 10 \\\n    ${MENU_ARRAY[@]} \\\n    2> ${TDIR}/rn_profile\n\n    CLEAR\n\n    PROFILE=\"$(cat ${TDIR}/rn_profile)\"\n    if [ ! -z \"$PROFILE\" ]\n    then\n      python3 ${SCDIR}/maint/install-profile.py --profile \"${PROFILED}/${PROFILE}.ini\"\n    else\n      exit 1\n    fi\n}\n\nrn_profile_chooser\n"
  },
  {
    "path": "dialog/retronas_fixperms.sh",
    "content": "#!/bin/bash\n\nset -u\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\ncd ${DIDIR}\n\nrn_get_dirs() {\n  COUNT=3\n  cd \"${OLDRNPATH}\"\n  find . -maxdepth 1 -type d | sed 's#^\\./##g' | grep -v ^\\.$ | sort | while read PATHITEM\n  do\n    printf \"%s\\n%s\\n\" \"${PATHITEM}\" \"${PATHITEM}\"\n    COUNT=$((${COUNT}+1))\n  done\n}\n\nrn_fix_perms() {\n  cd ${DIDIR}\n\n  local MENU_ARRAY=(\n    1 \"Back\"\n    all \"ALL\"\n    $(rn_get_dirs)\n  )\n\n  local MENU_BLURB=\"\\nPlease choose a directory below ${OLDRNPATH} to fix. \\\n  \\n\\nThis will reset all the file ownership to the user \\\"${OLDRNUSER}\\\" \\\n  \\n\\nPlease be careful, as this is irreversible.  If unsure, exit now.\"\n\n  DLG_MENU \"Fix Permissions\" $MENU_ARRAY 10 \"${MENU_BLURB}\" \n}\n\nCLEAR\nrn_fix_perms\ncase ${CHOICE} in\n  1)\n    EXIT_OK\n    ;;\n  *)\n    CLEAR\n    bash $SCDIR/static/permissions.sh ${CHOICE}\n    PAUSE\n    ;;\nesac\n"
  },
  {
    "path": "dialog/retronas_password.sh",
    "content": "#!/bin/bash\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\nMENU_NAME=update-password\ncd ${DIDIR}\n\nrn_retronas_password() {\n  source ${LIBDIR}/common.sh\n\n  READ_MENU_TDESC \"${MENU_NAME}\"\n\n  MENU_ARRAY=(\n    \"Password:\" 1 10 \"\"  1 20 20 20\n    \"Repeat:\"   2 10 \"\"  2 20 20 20\n  )\n\n  DLG_PASSWORD \"${MENU_TNAME}\" \"${MENU_ARRAY}\" 5 \"${MENU_BLURB}\"\n\n  # if we can't create the sub-window report it and exit\n  echo ${CHOICE[0]} | grep \"make sub-window\" &> /dev/null\n  if [ $? -eq 0 ]\n  then\n    echo \"Failed to create sub-window, if possible resize your terminal window and try again\"\n    PAUSE\n    exit\n  fi\n\n  PASS_ONE=${CHOICE[0]}\n  PASS_TWO=${CHOICE[1]}\n\n  if [ ! -z \"${PASS_ONE}\" ]\n  then\n    if [ \"${PASS_ONE}\" == \"${PASS_TWO}\" ]\n    then\n      CLEAR\n      /opt/retronas/scripts/static/update-passwd.sh \"${PASS_ONE}\"\n      PAUSE\n    else\n      RN_LOG \"Passwords do not match\"\n      PAUSE\n      rn_retronas_password\n    fi\n  else\n    RN_LOG \"Password was blank\"\n    EXIT_CANCEL\n  fi\n\n}\n\nCLEAR\nrn_retronas_password\n\n"
  },
  {
    "path": "dialog/romimportsystem.sh",
    "content": "#!/bin/bash\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\ncd ${DIDIR}\n\nCLEAR\n\nrn_import_system() {\n\n  local MENU_NAME=romimportsystem\n  READ_MENU_JSON \"${MENU_NAME}\"\n  READ_MENU_TDESC \"${MENU_NAME}\"\n  DLG_MENUJ \"${MENU_TNAME}\" 10 \"${MENU_BLURB}\"\n  \n    I=2\n    while IFS=\" \";read -ra LINE; do\n        if [[ \"${LINE[0]}\" == \"id:\" ]]; then\n            SYSTEMS+=${LINE[1]}' '\n            SYSTEM_ARR[$I]=${LINE[1]}\n            ((++I))\n        fi\n    done < <(/opt/retronas/scripts/romimport.sh -l)\n\n  while true\n  do\n    source $_CONFIG\n    \n    case ${CHOICE} in\n      01)\n        EXIT_OK\n      ;;\n      [0-9][0-9])\n        CLEAR\n        DROP_ROOT /opt/retronas/scripts/romimport.sh -t ${SYSTEM_ARR[10#$CHOICE]}\n        PAUSE\n      ;;\n      *)\n      EXIT_CANCEL\n      ;;\n      \n    esac\n    unset CHOICE\n  done\n}\n\nrn_import_system\n"
  },
  {
    "path": "dialog/tcpser_edit.sh",
    "content": "#!/bin/bash\n\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\nMENU_NAME=tcpser-edit\ncd ${DIDIR}\n\n\n# DEFAULTS\nDEVICE=\"/dev/ttyUSB0\"\nLISTEN=23456\nSPEED=9600\nINIT=Z\nADDN=\"\"\nMODE=\"PHYSICAL\"\n\nsource $_CONFIG\ncd ${DIDIR}\n\nMODE=${1:-PHYSICAL}\n\nrn_tcpser_edit() {\n\n  [ $MODE == \"VIRTUAL\" ] && DEVICE=25232\n\n  TCPSER_CONFIG_PATH=/opt/retronas/etc/tcpser\n  TCPSER_CONFIG=${TCPSER_CONFIG_PATH}/tcpser-${LISTEN}\n\n  READ_MENU_TDESC \"${MENU_NAME}\"\n\n  MENU_ARRAY=(\n    \"Device:\"      1 5 \"$DEVICE\"  1 20 20 20\n    \"Listen Port:\" 2 5 \"$LISTEN\"  2 20 20 20\n    \"Speed:\"       3 5 \"$SPEED\"   3 20 20 20\n    \"Init String:\" 4 5 \"$INIT\"    4 20 20 20\n    \"Additional\"   5 5 \"$ADDN\"    5 20 20 20\n    \"Mode\"         6 5 \"$MODE\"    6 20 20 20\n  )\n\n  DLG_FORM \"${MENU_TNAME}\" \"${MENU_ARRAY}\" 8 \"${MENU_BLURB}\"\n\n  [ ${#CHOICE[@]} -gt 0 ] && rn_tcpser_write_envfile\n\n}\n\nrn_tcpser_write_envfile() {\n\n  if [ \"${#CHOICE[1]}\" -gt 1 ]\n  then\n    local TCPSER_CONFIG=/opt/retronas/etc/tcpser/tcpser-${CHOICE[1]}\n\n    [ ${#CHOICE[0]} -le 1 ] && DATA[0]=$DEVICE\n    [ ${#CHOICE[2]} -le 1 ] && DATA[2]=$SPEED\n    [ ${#CHOICE[3]} -le 1 ] && DATA[3]=$INIT\n    [ ${#CHOICE[4]} -le 1 ] && DATA[4]=$ADDN\n    [ ${#CHOICE[5]} -le 1 ] && DATA[5]=$MODE\n\n    DEVSTR=\"d\"\n    [ ${DATA[5]} == \"VIRTUAL\" ] && DEVSTR=\"v\"\n\n    echo \"DEVICE=-${DEVSTR}${CHOICE[0]}\" > \"${TCPSER_CONFIG}\"\n    echo \"LISTEN=-p${CHOICE[1]}\" >> \"${TCPSER_CONFIG}\"\n    echo \"SPEED=-s${CHOICE[2]}\"  >> \"${TCPSER_CONFIG}\"\n    echo \"INIT=-i${CHOICE[3]}\"   >> \"${TCPSER_CONFIG}\"\n    echo \"ADDN=${CHOICE[4]}\"     >> \"${TCPSER_CONFIG}\"\n\n    EXIT_OK\n    \n  else\n    echo \"No data available for LISTEN port, so we won't be writing a file\"\n    PAUSE\n    rn_tcpser_edit\n  fi\n\n}\n\nrn_tcpser_edit\n\n"
  },
  {
    "path": "dialog/tcpser_status.sh",
    "content": "#!/bin/bash\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\nMENU_NAME=tcpser-service\n\ncd ${DIDIR}\n\nrn_tcpser_status() {\n  source $_CONFIG\n\n  local MODE=\"${1}\"\n  READ_MENU_TDESC \"${MENU_NAME}\"\n  MENU_ARRAY=(\n    \"Listen Port:\" 1 5 \"$LISTEN\"  2 20 20 20\n  )\n\n  DLG_FORM \"${MENU_TNAME}\" \"${MENU_ARRAY}\" 8 \"${MENU_BLURB}\"\n\n  [ ${#CHOICE[@]} -gt 0 ] && RN_SYSTEMD_$(echo ${MODE} | tr '[a-z]' '[A-Z]') tcpser@${CHOICE}.service\n  PAUSE\n}\n\n\nCLEAR\nrn_tcpser_status \"${1}\"\n"
  },
  {
    "path": "dialog/wifi_password.sh",
    "content": "#!/bin/bash\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\nMENU_NAME=update-wifi-password\ncd ${DIDIR}\n\nrn_wifi_password() {\n  source ${LIBDIR}/common.sh\n\n  READ_MENU_TDESC \"${MENU_NAME}\"\n\n  MENU_ARRAY=(\n    \"Password:\" 1 10 \"\"  1 20 20 20\n    \"Repeat:\"   2 10 \"\"  2 20 20 20\n  )\n\n  DLG_PASSWORD \"${MENU_TNAME}\" \"${MENU_ARRAY}\" 10 \"${MENU_BLURB}\"\n\n  PASS_ONE=${CHOICE[0]}\n  PASS_TWO=${CHOICE[1]}\n\n  if [ ! -z \"${PASS_ONE}\" ]\n  then\n    if [ \"${PASS_ONE}\" == \"${PASS_TWO}\" ]\n    then\n      CLEAR\n      /opt/retronas/scripts/static/wifi-update-passwd.sh \"${PASS_ONE}\"\n      PAUSE\n    else\n      RN_LOG \"Passwords do not match\"\n      PAUSE\n      rn_wifi_password\n    fi\n  else\n    RN_LOG \"Password was blank\"\n    EXIT_CANCEL\n  fi\n\n}\n\nCLEAR\nrn_wifi_password\n\n"
  },
  {
    "path": "dialog/zterm_edit.sh",
    "content": "#!/bin/bash\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\nMENU_NAME=zterm-edit\ncd ${DIDIR}\n\n[ ! -f /opt/zterm/zconfig ] && echo \"Install zterm first\" && PAUSE\n\n# DEFAULTS\nDEVICE=\"$(awk -F \"=\" '/^DEV/{print $2}' /opt/zterm/zconfig)\"\nSPEED=\"$(awk -F \"=\" '/^SPEED/{print $2}' /opt/zterm/zconfig)\"\nADDN=\"$(awk -F \"=\" '/^ADDN/{print $2}' /opt/zterm/zconfig)\"\nOUTDIR=/opt/zterm\n\nsource $_CONFIG\ncd ${DIDIR}\n\nrn_zterm_edit() {\n  READ_MENU_TDESC \"${MENU_NAME}\"\n\n  MENU_ARRAY=(\n    \"Device:\"      1 5 \"$DEVICE\"  1 20 20 20\n    \"Speed:\"       2 5 \"$SPEED\"   2 20 20 20\n    \"Additional\"   3 5 \"$ADDN\"    3 20 20 20\n  )\n\n  DLG_FORM \"${MENU_TNAME}\" \"${MENU_ARRAY}\" 8 \"${MENU_BLURB}\"\n\n  [ ${#CHOICE[@]}  -gt 1 ] && rn_zterm_write_config\n\n}\n\nrn_zterm_write_config() {\n\n  CLEAR\n  if [ ! -z ${CHOICE[0]} ] \n  then\n    echo \"Updating device to ${DEVSTR}${CHOICE[0]}\"\n    sed -i -r \"s#^DEV.+#DEV=${DEVSTR}${CHOICE[0]}#\" $OUTDIR/zconfig\n    sed -i -r \"s#^SPEED.+#SPEED=${DEVSTR}${CHOICE[1]}#\" $OUTDIR/zconfig\n    sed -i -r \"s#^ADDN.*#ADDN=${DEVSTR}${CHOICE[2]}#\" $OUTDIR/zconfig\n    sed -i -r \"s#^Condition.+#ConditionPathExists=${DEVSTR}${CHOICE[0]}#\" /usr/lib/systemd/system/zterm.service\n    systemctl daemon-reload\n    systemctl restart zterm.service\n  else\n    echo \"No change\" \n    PAUSE\n  fi\n  EXIT_OK\n}\n\nrn_zterm_edit\n"
  },
  {
    "path": "dist/install_preseed.sh",
    "content": "#!/bin/sh\n\n#\n# This is intended to be used to preseed a debian iso\n# as part of d-i preseed/late_command\n#\n# needs to be cleaned up or replaced, just for testing\n#\n\nif [ ! -f /opt/retronas/retronas_deployed ] && [ ! -f /opt/retronas/retronas_running ]\nthen\n    touch /opt/retronas/retronas_running\n    /usr/bin/curl -so /tmp/install_retronas.sh https://raw.githubusercontent.com/retronas/retronas/main/install_retronas.sh\n    /usr/bin/chmod a+x /tmp/install_retronas.sh\n    /tmp/install_retronas.sh\n\n    cd /opt/retronas/ansible\n\n    cp retronas_vars.yml.default retronas_vars.yml\n\n    /usr/bin/ansible-playbook -vv retronas_dependencies.yml\n    /usr/bin/ansible-playbook -vv install_filesystems.yml\n    /usr/bin/ansible-playbook -vv install_cockpit.yml\n    /usr/bin/ansible-playbook -vv install_cockpit-packages.yml\n    #/usr/bin/ansible-playbook -vv install_cockpit-retronas.yml\n\n    rm /opt/retronas/retronas_running\n    touch /opt/retronas/retronas_deployed\n\nfi"
  },
  {
    "path": "dist/retronas",
    "content": "#!/bin/bash\n\nsudo -E /opt/retronas/retronas.sh $*"
  },
  {
    "path": "install_retronas.sh",
    "content": "#!/bin/bash\n\nset -u\n\nGITREPO='https://github.com/retronas/retronas.git'\nFORCE=0\nTARGET=/opt/retronas\n\nMYID=$( whoami )\n\n_usage() {\n  echo \"Usage $0\" \n  echo \"-h this help\"\n  echo \"-o override git repo/branch to install from\"\n  echo \"-f force re-installation (EXPERT)\"\n  exit 0\n}\n\nif [ \"${MYID}\" != \"root\" ]\nthen\n  echo \"This script needs to be run as sudo/root\"\n  echo \"Please re-run:\"\n  echo \"sudo $0\"\n  exit 1\nfi\n\nOPTSTRING=\"fho:\"\nwhile getopts $OPTSTRING ARG\ndo\n  case $ARG in\n    h)\n      _usage\n      ;;\n    o)\n      GITREPO=\"${OPTARG}\"\n      ;;\n    f)\n      FORCE=1\n      ;;\n  esac\ndone\n\n# handle existing installations\n[ -f ${TARGET}/.git/config ] && [ $FORCE -eq 0 ] && echo \"Existing installation pass -f to overwrite\" && exit 1\n[ -f ${TARGET}/.git/config ] && [ $FORCE -eq 1 ] && echo \"Installation exists, -f passed, removing\" && rm -rf \"${TARGET}/\"\n\n\necho\necho \"Updating repo cache...\"\napt update\n\necho\necho \"Installing necessary tools...\"\napt install -y ansible git dialog jq pandoc lynx sudo\n\nif [ ! -f ${TARGET}/.git/config ]\nthen\n  echo\n  echo \"Downloading RetroNAS...from ${GITREPO}\"\n  cd /opt\n  git clone \"$GITREPO\"\n  chmod a+x /opt/retronas/retronas.sh\n\n  if [ $? -eq 0 ]\n  then\n\n    # docs\n    if [ ! -f ${TARGET}/doc/.git/config ]\n    then\n      echo \"Downloading RetroNAS documentation ...from ${GITREPO}\"\n      git clone https://github.com/retronas/retronas.wiki.git  ${TARGET}/doc\n    fi\n\n    #installing a simple starup script\n    cp /opt/retronas/dist/retronas /usr/local/bin/retronas\n    chmod a+x /usr/local/bin/retronas\n\n    echo\n    echo \"All done.  You can now run the RetroNAS config tool with the following command:\"\n    echo\n    echo \"retronas\"\n    echo\n  else\n    echo \"Installation FAILED, check previous messages\"\n  fi\nfi\n"
  },
  {
    "path": "lib/ansible_runner.sh",
    "content": "#!/bin/bash\n#\n# Ansible wrapper to trigger playbooks \n# or roles through tags\n#\nset -u\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\n\nPREFIX=install_\nSUFFIX=.yml\nANSCMD=\"/usr/bin/ansible-playbook\"\n\ncd \"${ANDIR}\"\n\nfunction run_ansible {\n    local PLAYBOOK=\"${1}\"\n    [ ! -f ${PLAYBOOK} ] && echo \"Failed to find playbook: ${PLAYBOOK}\" && exit 1\n    ${ANSCMD} \"${PLAYBOOK}\"\n}\n\n[ -z \"${1}\" ] && echo \"No options passed\" && exit 1\n[ ! -f ${ANSCMD} ] && echo \"Ansible is not installed, install it first\" && exit 1\n\nIFS=':' read -r -a PLAYBOOKS <<< \"${1}\"\n[ ${#PLAYBOOKS[@]} -eq 0 ] && echo \"Failed to read args, exiting\"  && exit 1\n\nrun_ansible ${ANDIR}/retronas_dependencies.yml\nfor PLAYBOOK in ${PLAYBOOKS[@]}\ndo\n    run_ansible \"${ANDIR}/${PREFIX}${PLAYBOOK}${SUFFIX}\"\ndone\n"
  },
  {
    "path": "lib/common.sh",
    "content": "#!/bin/bash\n\nset -u\n\nexport SC=\"systemctl --no-pager --full\"\n\n###############################################################################\n#\n# Logging\n#\n###############################################################################\n\n#\n# use RN_LOG instead of echo, so we can centralise logging approach\n#\nRN_LOG() {\n    echo \"${1}\"\n}\n\n###############################################################################\n#\n# Package\n#\n###############################################################################\nCHECK_PACKAGE_CACHE() {\n    case $1 in\n        *)\n            # check if apt was updated in the last 24 hours\n            if find /var/cache/apt -maxdepth 1 -type f -mtime -1 -exec false {} +\n            then\n                echo \"APT repositories are old, syncing...\"\n                apt update\n            fi\n            ;;\n    esac\n}\n\n\n###############################################################################\n#\n# GENERAL reusable functions\n#\n###############################################################################\n\n#\n# Compare two values\n#\nCOMPARE_VALUES() {\n    local STROLD=$1\n    local STRNEW=$2\n    export CONFIRM=0\n\n    if [ ! -z ${STRNEW} ] \n    then\n      if [ \"${STRNEW}\" != \"${STROLD}\" ]\n      then\n        # Confirm the input\n        #CHECK_USER_EXISTS $STRNEW $STROLD\n        export CONFIRM=1\n      else\n        CLEAR\n        RN_LOG \"Item not changed\"\n        PAUSE\n      fi\n    else \n      CLEAR\n      RN_LOG \"No values to process\"\n      PAUSE\n    fi\n}\n\n#\n# CONFIRM A VALUE\n#\nCONFIRM_VALUE() {\n    echo $1\n}\n\n###############################################################################\n#\n# PRIVILEGE handlers\n#\n###############################################################################\n\n## If this is run as root, switch to our RetroNAS user\nDROP_ROOT() {\n  sudo -u ${OLDRNUSER} -s $*\n}\n\n### Check if we have the correct privs for op\nCHECK_ROOT() {\n    [ $UID -ne 0 ] && echo \"You must run this as root, try sudo <command>\" && exit 1\n}\n\n###############################################################################\n#\n# LANGUAGE handlers\n#\n###############################################################################\n\n### Get LANG file\nGET_LANG() {\n    [ ! -z \"${LANG}\" ] && RNLANG=$(echo $LANG | awk -F'_' '{print $1}')\n    [ -z ${RNLANG} ] && RNLANG=\"en\"\n    \n    if [ -f ${LANGDIR}/${RNLANG} ]\n    then\n        source ${LANGDIR}/${RNLANG}\n    else\n        echo \"Failed to load language file, check config is complete\"\n        exit 1\n    fi\n\n}\n\n###############################################################################\n#\n# MENU data loaders\n#\n###############################################################################\n\n#\n# Read menu items from the json config\n# export the data for use in dialogs\n#\nREAD_MENU_TDESC() {\n    export MENU_TITLE=\"$1\"\n    export MENU=\"$2\"\n\n    # handle submenus\n    [ -z \"$MENU\" ] && MENU=\"menu\"\n\n    if [ -f ${RNJSON}/${MENU_TITLE}.json ]\n    then\n        local MENU_TDESC_DATA=$(<${RNJSON}/${MENU_TITLE}.json jq -r \".\\\"${MENU}\\\" | \\\"\\(.title);\\(.description)\\\"\")\n    elif [ -f ${RNJSONOLD} ]\n    then\n        local MENU_TDESC_DATA=$(<${RNJSONOLD} jq -r \".dialog.\\\"${MENU_TITLE}\\\" | \\\"\\(.title);\\(.description)\\\"\")\n    else\n        local MENU_TDESC_DATA=$(<${RNJSON}/main.json jq -r \".menu | \\\"\\(.title);\\(.description)\\\"\")    \n    fi\n    local IFS=$'\\n'\n\n    IFS=\";\" read -r -a PIECES <<< ${MENU_TDESC_DATA}\n    export MENU_TNAME=${PIECES[0]}\n    # eval ... yes i'm so terribly sorry\n    export MENU_BLURB=$(eval \"echo $(echo ${PIECES[@]:1} | sed 's/|/\\\\\\\\n/g')\" )\n}\n\n#\n# Read menu items from the json config\n# export the data for use in dialogs\n#\nREAD_MENU_JSON() {\n    local MENU_TITLE=\"${1}\"\n    local MENU=\"${2}\"\n\n    # handle submenus\n    [ -z \"$MENU\" ] && MENU=\"menu\"\n\n    if [ -f ${RNJSON}/${MENU_TITLE}.json ]\n    then\n        export MENU_DATA=$(<${RNJSON}/${MENU_TITLE}.json jq -r \".\\\"${MENU}\\\".items[] | \\\"\\(.index)|\\(.title)|\\(.description)|\\(.type)\\\"\")\n    elif [ -f ${RNJSONOLD} ]\n    then\n        export MENU_DATA=$(<${RNJSONOLD} jq -r \".dialog.\\\"${MENU}\\\".items[] | \\\"\\(.index)|\\(.title)|\\(.description);\\\"\")\n    else\n        export MENU_DATA=$(<${RNJSON}/main.json jq -r \".menu.items[] | \\\"\\(.index)|\\(.title)|\\(.description);\\\"\")\n    fi\n}\n\n#\n# Looks up the menu item in the json file. returns both type and command values\n# type determines the action taken\n# command is a colon separated list of actions taken by helper functions\n#\nREAD_MENU_COMMAND() {\n    local MENU_NAME=\"$1\"\n    local MENU_CHOICE=$2\n\n    cd \"${RNDIR}\"\n\n    if [ -f ${RNJSON}/${MENU_TITLE}.json ]\n    then\n        MENU_DATA=$(<${RNJSON}/${MENU_TITLE}.json jq -r \".menu.items[] | select(.index == \\\"${MENU_CHOICE}\\\") | \\\"\\(.type)|\\(.command)|\\(.args)\\\"\")\n    elif [ -f ${RNJSONOLD} ]\n    then\n        MENU_DATA=$(<${RNJSONOLD} jq -r \".dialog.${MENU_NAME}.items[] | select(.index == \\\"${MENU_CHOICE}\\\") | \\\"\\(.type)|\\(.command)\\\"\")\n    else\n        MENU_DATA=$(<${RNJSON}/main.json jq -r \".menu.items[] | select(.index == \\\"${MENU_CHOICE}\\\") | \\\"\\(.type)|\\(.command)|\\(.args)\\\"\")\n    fi\n\n    IFS=\"|\" read -r -a PIECES <<< ${MENU_DATA}\n    local MENU_TYPE=${PIECES[0]}\n    local MENU_SELECT=${PIECES[1]}\n    local MENU_ARGS=${PIECES[@]:2}\n\n    CLEAR\n    if [ ! -z \"${MENU_SELECT}\" ] && [ \"${MENU_SELECT}\" != \"null\" ]\n    then \n        case $MENU_TYPE in\n            install)\n                RN_INSTALL_EXECUTE $MENU_SELECT\n                ;;\n            modal)\n                EXEC_SCRIPT \"m-${MENU_SELECT}\"\n                ;;\n            dialog)\n                EXEC_SCRIPT \"d-${MENU_SELECT}\"\n                ;;\n            dialog_input)\n                EXEC_SCRIPT \"i-${MENU_SELECT}\"\n                ;;\n            dialog_yn)\n                EXEC_SCRIPT \"y-${MENU_SELECT}\"\n                ;;\n            form)\n                EXEC_SCRIPT \"f-${MENU_SELECT}\" $MENU_ARGS\n                ;;\n            script)\n                EXEC_SCRIPT $MENU_SELECT $MENU_ARGS\n                ;;\n            script-static)\n                EXEC_SCRIPT \"s-${MENU_SELECT}\"\n                ;;\n            service_status)\n                RN_SYSTEMD_STATUS \"${MENU_SELECT}\"\n                ;;\n            service_enable_start)\n                RN_SYSTEMD_ENABLE \"${MENU_SELECT}\"\n                RN_SYSTEMD_START \"${MENU_SELECT}\"\n                ;;\n            service_start)\n                RN_SYSTEMD_START \"${MENU_SELECT}\"\n                ;;\n            service_start_follow)\n                RN_SYSTEMD_START \"${MENU_SELECT}\"\n                RN_JOURNAL_FOLLOW \"${MENU_SELECT}\"\n                ;;\n            service_restart)\n                RN_SYSTEMD_RESTART \"${MENU_SELECT}\"\n                ;;\n            service_disable_stop)\n                RN_SYSTEMD_DISABLE \"${MENU_SELECT}\"\n                RN_SYSTEMD_STOP \"${MENU_SELECT}\"\n                ;;\n            service_stop)\n                RN_SYSTEMD_STOP \"${MENU_SELECT}\"\n                ;;\n            menu)\n                \"${MENU_SELECT}\"\n                ;;\n            command)\n                bash -c \"${MENU_SELECT}\"\n                ;;\n            documentation)\n                RN_DOCUMENTATION \"${MENU_SELECT}\"\n                ;;\n            *)\n                echo \"Not supported, why are you here?\"\n                PAUSE\n                ;;\n        esac\n    else\n        echo \"Failed to select item for $MENU_CHOICE\"\n        PAUSE\n    fi\n    unset MENU_SELECT\n}\n\n###############################################################################\n#\n# EXIT handlers\n#\n###############################################################################\n\nEXIT_OK() {\n    CLEAR\n    exit 0\n}\n\n\nEXIT_CANCEL() {\n    CLEAR\n    exit 1\n}\n\n### Clear function, standardised\nCLEAR() {\n    clear\n}\n\n### Wait for user input\nPAUSE() {\n    echo \"${PAUSEMSG}\"\n    read -s\n}\n\n###############################################################################\n#\n# COMMAND handlers\n#\n###############################################################################\n\n### Run a script\nEXEC_SCRIPT() {\n    local SCRIPT=\"${1}\"\n\n    #CLEAR\n    #CHECK_PACKAGE_CACHE\n\n    CLEAR\n    shift\n    /opt/retronas/lib/script_runner.sh \"${SCRIPT}\" $*\n\n    cd ${DIDIR}\n    unset SCRIPT\n}\n\n#\n# Install Ansible Dependencies, runs with every installer\n#\n# !!! this function will be deprecated\n#\nRN_INSTALL_DEPS() {\n    source $_CONFIG\n    cd ${ANDIR}\n\n    CLEAR\n    CHECK_PACKAGE_CACHE\n\n    ansible-playbook -vv retronas_dependencies.yml\n    cd ${DIDIR}\n}\n\n#\n# Run the playbook\n#\nRN_INSTALL_EXECUTE() {\n    source $_CONFIG\n    local PLAYBOOK=$1\n\n    CLEAR\n    CHECK_PACKAGE_CACHE\n\n    CLEAR\n    /opt/retronas/lib/ansible_runner.sh \"${PLAYBOOK}\"\n    PAUSE\n\n    #cd ${ANDIR}\n    #ansible-playbook -vv \"${PLAYBOOK}\"\n    cd ${DIDIR}\n    unset PLAYBOOK\n}\n\n#\n# Run the pandoc/lynx\n#\nRN_DOCUMENTATION() {\n    source $_CONFIG\n    local DOCUMENTATION=$1\n\n    CLEAR\n    /opt/retronas/lib/markup_runner.sh \"${DOCUMENTATION}\"\n\n    cd ${DIDIR}\n    unset DOCUMENTATION\n}\n\n\n###############################################################################\n#\n# SERVICE query/type handlers\n#\n###############################################################################\n\n#\n# Service status formatting\n#\nRN_SERVICE_STATUS() {\n    local CMD=\"$1\"\n\n    CLEAR\n    echo \"${CMD}\"\n    echo ; eval $CMD ; echo\n    #PAUSE\n}\n\n#\n# SYSTEMD status checks\n#\nRN_SYSTEMD_STATUS() {\n    RN_SYSTEMD \"${1}\" \"status\"\n    PAUSE\n}\n\n#\n# SYSTEMD enable\n#\nRN_SYSTEMD_ENABLE() {\n    RN_SYSTEMD \"${1}\" \"enable\"\n}\n\n#\n# SYSTEMD start\n#\nRN_SYSTEMD_START() {\n    RN_SYSTEMD \"${1}\" \"start\"\n}\n\n#\n# SYSTEMD restart\n#\nRN_SYSTEMD_RESTART() {\n    RN_SYSTEMD \"${1}\" \"restart\"\n}\n\n#\n# SYSTEMD disable\n#\nRN_SYSTEMD_DISABLE() {\n    RN_SYSTEMD \"${1}\" \"disable\"\n}\n\n#\n# SYSTEMD stop\n#\nRN_SYSTEMD_STOP() {\n    RN_SYSTEMD \"${1}\" \"stop\"\n}\n\n#\n# SYSTEMD\n#\nRN_SYSTEMD() {\n    local SERVICE=\"$1\"\n    local COMMAND=\"${2:-status}\"\n\n    RN_SERVICE_STATUS \"${SC} ${COMMAND} ${SERVICE}\"\n\n}\n\n#\n# JOURNAL follow\n#\nRN_JOURNAL_FOLLOW() {\n    journalctl --follow -u \"${1}\"\n}\n\n#\n# DIRECTLY call a status command, and pass args\n#\nRN_DIRECT_STATUS() {\n    source $_CONFIG\n    local SERVICE=\"$1\"\n    local ARGS=\"$2\"\n\n    if [ -x \"$(which $SERVICE)\" ]\n    then\n    RN_SERVICE_STATUS \"${SERVICE} ${ARGS}\"\n    fi\n\n}\n\n\n###############################################################################\n#\n# DIALOG builder functions\n# based on: https://stackoverflow.com/questions/4889187/dynamic-dialog-menu-box-in-bash\n#\n###############################################################################\n\n#\n# MENU\n#\nDLG_MENUJ() {\n\n    local IFS=$'\\n'\n    local TITLE=\"$1\"\n    local MENU_H=$2\n    local MENU_BLURB=$3\n\n    local MENU_DESC=\"${IPADDMSGNO}${MENU_BLURB}\"\n\n    DIALOG=(dialog \\\n            --backtitle \"${APPNAME}\" \\\n            --title \"${APPNAME} ${TITLE} Menu\" \\\n            --clear \\\n            --no-shadow \\\n            --menu \"$MENU_DESC\" ${MW} ${MH} ${MENU_H})\n\n    declare -a MENU_ARRAY\n    for ITEM in ${MENU_DATA[@]}\n    do\n        IFS=\"|\" read -r -a PIECES <<< $ITEM\n        INDEX=${PIECES[0]}\n        TITLE=${PIECES[1]}\n        DESC=${PIECES[2]}\n        TYPE=${PIECES[3]}\n\n        [ \"${TYPE}\"  == \"dialog\" ] && DESC=\"${DESC} >>\"\n        MENU_ARRAY+=(${INDEX} \"${TITLE} - ${DESC}\")\n    done\n\n    export CHOICE=$(\"${DIALOG[@]}\" \"${MENU_ARRAY[@]}\" 2>&1 >/dev/tty)\n    unset MENU_ARRAY\n\n}\n\n\n#\n# MENU\n#\nDLG_MENU() {\n\n    local IFS=$'\\n'\n    local TITLE=\"$1\"\n    MENU_ARRAY=$2\n    local MENU_H=$3\n    local MENU_BLURB=$4\n\n    local MENU_DESC=\"${IPADDMSGNO}${MENU_BLURB}\"\n\n    DIALOG=(dialog \\\n            --backtitle \"${APPNAME}\" \\\n            --title \"${APPNAME} ${TITLE} Menu\" \\\n            --clear \\\n            --no-shadow \\\n            --menu \"$MENU_DESC\" ${MW} ${MH} ${MENU_H})\n\n    export CHOICE=$(\"${DIALOG[@]}\" \"${MENU_ARRAY[@]}\" 2>&1 >/dev/tty)\n    unset MENU_ARRAY\n\n}\n\n#\n# YES/NO\n#\nDLG_YN() {\n    local IFS=$'\\n'\n    local TITLE=\"$1\"\n    local MENU_BLURB=$2\n\n    local MENU_DESC=\"${IPADDMSGNO}${MENU_BLURB}\"\n\n    DIALOG=(dialog \\\n    --backtitle \"${APPNAME}\" \\\n    --title \"${APPNAME} ${TITLE} Menu\" \\\n    --clear \\\n    --no-shadow \\\n    --defaultno \\\n    --yesno \"${MENU_DESC}\" ${MW} ${MH})\n\n    \"${DIALOG[@]}\" 2>&1 >/dev/tty\n    export CHOICE=$?\n}\n\n#\n# DIRECTORY SELECTOR\n#\nDLG_DSELECT() {\n    local IFS=$'\\n'\n    local TITLE=\"$1\"\n    local MENU_BLURB=$2\n\n    local MENU_DESC=\"${IPADDMSGNO}${MENU_BLURB}\"\n\n    DIALOG=(dialog \\\n    --backtitle \"${APPNAME}\" \\\n    --title \"${APPNAME} ${TITLE} Menu\" \\\n    --clear \\\n    --no-shadow \\\n    --dselect \"${MENU_BLURB}\" ${MW} ${MH})\n\n    export CHOICE=$(\"${DIALOG[@]}\" 2>&1 >/dev/tty)\n}\n\n#\n# INPUTBOX SELECTOR\n#\nDLG_INPUTBOX() {\n    local IFS=$'\\n'\n    local TITLE=\"$1\"\n    local MENU_BLURB=$2\n    local MENU_INIT=$3\n\n    local MENU_DESC=\"${IPADDMSGNO}${MENU_BLURB}\"\n\n    DIALOG=(dialog \\\n    --backtitle \"${APPNAME}\" \\\n    --title \"${APPNAME} ${TITLE} Menu\" \\\n    --clear \\\n    --no-shadow \\\n    --inputbox \"${MENU_BLURB}\" ${MW} ${MH} $MENU_INIT)\n\n    export CHOICE=$(\"${DIALOG[@]}\" 2>&1 >/dev/tty)\n}\n\n#\n# PASSWORD INPUT\n#\nDLG_PASSWORD() {\n    local IFS=$'\\n'\n    local TITLE=\"$1\"\n    MENU_ARRAY=$2\n    local MENU_H=$3\n    local MENU_BLURB=$4\n\n    local MENU_DESC=\"${IPADDMSGNO}${MENU_BLURB}\"\n\n    DIALOG=(dialog \\\n        --backtitle \"${APPNAME}\" \\\n        --title \"${APPNAME} ${TITLE} Menu\" \\\n        --clear \\\n        --no-shadow \\\n        --insecure \\\n        --passwordform \"\\n$MENU_DESC\" ${MW} ${MH} ${MENU_H})\n\n    export CHOICE=($(\"${DIALOG[@]}\" \"${MENU_ARRAY[@]}\" 2>&1 >/dev/tty))\n    unset MENU_ARRAY\n}\n\n\n#\n# FORM INPUT\n#\nDLG_FORM() {\n    local IFS=$'\\n'\n    local TITLE=\"$1\"\n    MENU_ARRAY=$2\n    local MENU_H=$3\n    local MENU_BLURB=$4\n\n    local MENU_DESC=\"${IPADDMSGNO}${MENU_BLURB}\"\n\n    DIALOG=(dialog \\\n        --backtitle \"${APPNAME}\" \\\n        --title \"${APPNAME} ${TITLE} Menu\" \\\n        --clear \\\n        --no-shadow \\\n        --form \"\\n$MENU_DESC\" ${MW} ${MH} ${MENU_H})\n\n    export CHOICE=($(\"${DIALOG[@]}\" \"${MENU_ARRAY[@]}\" 2>&1 >/dev/tty))\n    unset MENU_ARRAY\n}\n\nset +u\n"
  },
  {
    "path": "lib/markup_runner.sh",
    "content": "#!/bin/bash\nset -u\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nPBIN=/usr/bin/pandoc\nLBIN=/usr/bin/lynx\n\nINPUT=${1}\n\nif [ ! -f ${RNDOC}/${INPUT} ]\nthen\n    #echo \"Documentation has not been installed, will display README.md\"\n    INPUT=\"${RNDIR}/README.md\"\nelse\n    INPUT=\"${RNDOC}/${INPUT}\"\nfi\n\n${PBIN} \"${INPUT}\" | ${LBIN} -stdin"
  },
  {
    "path": "lib/script_runner.sh",
    "content": "#!/bin/bash\n#\n# SCRIPTrunner\n#\n# This is called by the cockpit webui to run scripts, it should only ever run \n# scripts from the scripts directory and should sanitize the input\n#\n\n#set -u -x\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\nPREFIX=\nSUFFIX=.sh\nKEY=\"${1}\"\nVALUE=\"${2:-}\"\nX_SANITIZE=1\n\n# check for static type\nTYPE=${KEY::2}\nSCRIPT=${KEY:2}\ncase ${TYPE} in\n    s-)\n        # STATIC\n        SCDIR=\"${SCDIR}/static\"\n        ;;\n    m-)\n        # MODAL\n        SCDIR=\"${DIDIR}\"\n        ;;\n    i-)\n        # DIALOG (input)\n        SCDIR=\"${DIDIR}\"\n        SCRIPT=\"d_input\"\n        X_SANITIZE=0\n        VALUE=${KEY:2}\n        ;;\n    d-)\n        # DIALOGS (auto run)\n        SCDIR=\"${DIDIR}\"\n        SCRIPT=\"d_menu\"\n        X_SANITIZE=0\n        VALUE=${KEY:2}\n        ;;\n    f-)\n        # FORMS\n        SCDIR=\"${DIDIR}\"\n        PARTS=($(echo \"${KEY}\" | sed 's/\\// /g'))\n        SCRIPT=${PARTS[0]:2}\n        X_SANITIZE=0\n        VALUE=\"${PARTS[1]}\"\n        ;;\n    y-)\n        # DIALOGS (yes/no)\n        SCDIR=\"${DIDIR}\"\n        SCRIPT=\"d_yn\"\n        X_SANITIZE=0\n        VALUE=${KEY:2}\n        ;;\n    *)\n        # EVERYTHING ELSE\n        SCRIPT=\"${KEY}\"\n        ;;\nesac\n\n## make this better, its a hail mary anyway since the script is called from client-side\nSANITIZED=$(echo \"${SCRIPT}\" | sed 's/[;]//g')\n\n## ADDITIONAL\nif [ $X_SANITIZE -eq 1 ]\nthen\n    SANITIZED=$(echo \"${SCRIPT}\" | sed 's/[\\.]//g')\nfi\n\n# build script name\nSCRIPT=\"${PREFIX}${SANITIZED}${SUFFIX}\"\ncd \"${SCDIR}\"\n\n[ -z \"${1}\" ] && echo \"No options passed\" && exit 1\n[ ! -f ${SCRIPT} ] && echo \"Failed to find script for ${SCRIPT}\" && PAUSE && exit 1\n[ -z ${SCDIR} ] && echo \"SCDIR cannot be empty, something is terribly wrong\" && exit 2\n\nshift\n# this can be abused, find a better option\nbash ${SCDIR}/${SCRIPT} ${VALUE} $* 2>&1\n"
  },
  {
    "path": "retronas.sh",
    "content": "#!/bin/bash\n\n# ansible 2.14 workaround\nexport LC_ALL=${LANG}\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\nDISABLE_GITOPS=0\nCF=\"$ANCFG\"\nTCHOICE=\"\"\n\n#\n# CHECK we are running as ROOT\n#\nCHECK_ROOT\n\n_usage() {\n  echo \"Usage $0\" \n  echo \"-h this help\"\n  echo \"-d show disclaimer\"\n  echo \"-g disable git operations\"\n  echo \"-l show license\"\n  echo \"-t terminal choice (current|vterm)\"\n  echo \"-v run in vt100 mode\"\n  echo \"-a ask for terminal selection at statup\"\n  exit 0\n}\n\nOPTSTRING=\"hdgvat:\"\nwhile getopts $OPTSTRING ARG\ndo\n  case $ARG in\n    h)\n      _usage\n      ;;\n    g)\n      DISABLE_GITOPS=1\n      ;;\n    t)\n      TCHOICE=${\"\":-OPTARG}\n      ;;\n    v)\n      TCHOICE=vt100\n      ;;\n    a)\n      TICHOICE=\"ASK\"\n      ;;\n    d)\n      # redisplay agreement\n      [ -f $AGREEMENT ] && rm -f $AGREEMENT\n      ;;\n    l)\n      # display license\n      cat $RNDIR/LICENSE\n      ;;\n    *)\n      _usage\n      ;;\n  esac\ndone\n\n#### DO NOT TOUCH THE SYSTEM UNTIL USER AGREES TO DISCLAIMER\nif [ ! -f $AGREEMENT ]\nthen\n  bash $DIDIR/disclaimer.sh\n  [ $? -ne 0 ] && echo \"User did not accept terms of use, exiting\" && EXIT_CANCEL\nfi\n\n### ANSIBLE_VARS\ncd $RNDIR\n[ ! -f \"${ANCFG}\" ] && cp \"${ANCFG}.default\" \"${ANCFG}\"\n#\n# MIGRATIONS\n#\nbash ${PCHDIR}/git-config.sh\nbash ${PCHDIR}/install-jq.sh\nbash ${PCHDIR}/update-retronas_vars.sh\nbash ${PCHDIR}/new-startup-file.sh\nbash ${PCHDIR}/cache-dir.sh\n#\n# END MIGRATIONS\n#\n\n### source the config to update vars on first run\nsource $_CONFIG\n\n### LANG UTF-8 patch (this probably needs more testing)\nexport LANG=$(echo $LANG | sed -r 's/(\\.| )UTF(-?)8//gi')\n\n### DIALOG display patch for NCURSES UTF-8, might cause other issues with other tools\nexport NCURSES_NO_UTF8_ACS=1\n\n\n### Manage install through git\nif [ $DISABLE_GITOPS -eq 0 ] && [ ! -f ${USER_CONFIG}/disable_gitops ] && [ -f ${RNDIR}/.git/config ]\nthen\n  echo \"Fetching latest RetroNAS scripts...\"\n  git config pull.rebase false\n  git reset --hard HEAD\n  git pull\n\n  if [ -f \"${RNDOC}/Ideas.md\" ]\n  then\n    echo \"Updating local documentation...\"\n    cd $RNDOC\n    git pull\n    cd -\n  fi\nelse\n  echo \"Skipping git updates, git operations were disabled\"\nfi\n\n\n### Make sure log dir exists\n[ ! -d $LOGDIR ] && mkdir $LOGDIR && chmod 755 $LOGDIR\n\n### Set term emulation,\nRNSECONDS=3\nif [ \"${TICHOICE}\" == \"ASK\" ]\nthen\n  CLEAR\n  echo\n  echo \"Terminal encoding:\"\n  echo\n  echo \"1) Current - ${TERM} (default in 3s)\"\n  echo \"2) vt100 (best for telnet and retro computers)\"\n  echo\n  read -r $TCHOICE -t 5 -n 1 -p \"Please choose: \"\nfi\n\ncase \"${TCHOICE}\" in\n  VT100|vt100|2*)\n    echo \"Setting TERM to vt100\"\n    export TERM=vt100\n    ;;\n  *)\n    echo \"Leaving TERM as ${TERM}\"\n    ;;\nesac\n\n### Start RetroNAS\necho \"Running RetroNAS...\"\ncd $DIDIR\n\n### check default user exists\nif ! id $OLDRNUSER &>/dev/null\nthen\n  echo -e \"The currently configured USER $OLDRNUSER does not exist on this system you will now be prompted to update the config\"\n  PAUSE\n  bash d_input.sh update-user\nfi\n\n### check default group exists\nif ! getent group $OLDRNGROUP &>/dev/null\nthen\n  echo -e \"The currently configured GROUP $OLDRNGROUP does not exist on this system you will now be prompted to update the config\"\n  PAUSE\n  bash -c d_input.sh update-group\nfi\n\nbash d_menu.sh main\n"
  },
  {
    "path": "scripts/maint/git-changes-summary.sh",
    "content": "#!/bin/bash\n\n#\n# Helper script to build a basic change log for pull requests\n# has to be a better way to do this - sairuk\n# \n\nMAIN=$(git log origin/main --oneline -n1 --pretty=\"format:%h\")\nCOMMIT=$(git log --oneline --pretty=\"format:%h\" | grep -B1 $MAIN | head -n1)\nTESTING=$(git log origin/testing --oneline -n1 --pretty=\"format:%h\")\n\ngit log --oneline --pretty=\"format:* %s [%an]\" --ancestry-path ${COMMIT}..${TESTING}\ngit log --oneline --pretty=\"format:* %s [%an]\" ${COMMIT} -n1\n"
  },
  {
    "path": "scripts/maint/install-profile.py",
    "content": "#!/usr/bin/env python3\n\nimport os\nimport json\nimport argparse\nimport subprocess\n\nfrom configparser import ConfigParser\n\n\nrn_dir = \"/opt/retronas\"\nrn_ansible_runner = os.path.join(rn_dir, \"lib/ansible_runner.sh\")\nrn_section = \"package\"\n\n\ndef ini2dict(profile):\n    if os.path.exists(profile):\n        config= ConfigParser()\n        config.read(profile)\n        return config\n    else:\n        print(\"Couldn't read from profile: %s\" % profile)\n    return None\n\ndef ansible_run(profile):\n    p = subprocess.Popen([rn_ansible_runner, profile])\n    p.wait()\n    return\n\ndef main(args):\n    config = ini2dict(args.profile)\n    if config is not None:\n        if rn_section in config.keys():\n            for item in config[rn_section]:\n                ansible_run(item)\n        else:\n            print(\"Incompatible profile, could not find %s entry\" % rn_section)\n    return\n\n\n\nif __name__ == \"__main__\":\n    parser = argparse.ArgumentParser(description='replay installation profile')\n    parser.add_argument('--profile', help='profile name to replay', type=str, required=True)\n    args = parser.parse_args()\n    main(args)"
  },
  {
    "path": "scripts/maint/migrate_romdirs.py",
    "content": "#!/usr/bin/env python3\n#\n# hacky little script to see if the yaml \n# to migrate directories based on last entry\n# in retronas_systems\n#\nimport yaml\nimport argparse\nimport os\nimport shutil\n\nRN_VARS=\"../../ansible/retronas_vars.yml\"\nRN_SYSTEMS=\"../../ansible/retronas_systems.yml\"\nRN_IGNORE = [\n    \"system_map\",\n    \"system_links\",\n    \"system_template\", \n    \"system_unsupported\",\n    \"system_idsoftware\",\n    \"system_tools\"\n    ]\nRN_ROMDIR='roms'\n\ndef _log(s, level=0):\n    log_level = \"INFO\"\n    if level >= 1:\n        log_level = \"WARN\"\n    elif level >= 2:\n        log_level = \"ERRR\"\n    \n    print(\"[%s] %s\" % (log_level, s))\n\ndef read_yaml(ydata):\n    with open(ydata,'r') as file:\n        try:\n            return yaml.safe_load(file)\n        except yaml.YAMLError as e:\n            _log(e)\n            return None\n\ndef process_dir(dirname, tree=[]):\n    # loop over the old dir files\n    _log(\"Building entries for %s\" % dirname)\n    dir_contents = os.listdir(dirname)\n    for entry in dir_contents:\n        path = os.path.join(dirname, entry)\n        if os.path.isdir(path):\n            process_dir(path, tree)\n        else:\n            tree.append(path)\n            path = None\n    return tree\n\ndef main(args):\n    yaml_data = None\n\n    if not os.path.exists(RN_VARS):\n        _log(\"vars file not found did you start retronas yet?\", 2)\n        exit(1)\n\n    rn_vars = read_yaml(RN_VARS)\n    rn_path = rn_vars['retronas_path']\n    \n    rn_systems = read_yaml(RN_SYSTEMS)\n\n    if rn_systems is not None:\n        for manufacturer in rn_systems:\n            if manufacturer not in RN_IGNORE:\n                for system in rn_systems[manufacturer]:\n\n                    if system['last'] != \"\":\n                        entries = []\n                        # yes our dest here is the src key\n                        dest = os.path.join(rn_path,RN_ROMDIR,system['src'])\n                        # last location of this system\n                        last = os.path.join(rn_path,RN_ROMDIR,system['last'])\n\n                        move = False\n                        move_files = False\n                        if os.path.exists(last) and os.path.isdir(last):\n                            \n                            # parent directory\n                            parent = os.path.dirname(dest)\n                            if not os.path.isdir(parent):\n                                _log(\"Creating parent directory: %s\" % parent)\n                                os.makedirs(parent)\n\n                            # if dest is a symlink remove it\n                            if os.path.islink(dest):\n                                _log(\"Destination is a symlink, removing: %s\" % dest)\n                                os.remove(dest)\n                                move = True\n\n                            # move if dest isn't a dir\n                            if not os.path.isdir(dest):\n                                _log(\"Dest %s, doesn't exist, this is good we can move to this location\" % dest)\n                                move = True\n                            else:\n                                _log(\"Can't move %s, already exists: %s, already migrated? (try --file-mode?)\" % (last, dest), 1)\n                                if args.file_mode:\n                                    _log(\"We are in file-mode so flagging to move files instead for %s\" % last)\n                                    move_files = True\n                                move = False\n\n                            # if dest is a file cancel dont move\n                            if os.path.isfile(dest):\n                                _log(\"%s is a file, check and remove this manually\" % dest, 1)\n                                move = False\n\n                            # dir mode\n                            if move and not move_files:\n                                try:\n                                    _log(\"Moving %s to %s (file-mode not needed)\" % (last, dest))\n                                    shutil.move(last, dest)\n                                except:\n                                    _log(\"Move failed for %s\" % (last), 1)\n                            else:\n                                _log(\"Flagged for no dir-mode move: %s\" % last)\n\n\n                            # file mode ... though shutil.move was going to cover this but OK.\n                            if not move and move_files:\n                                _log(\"We are in file-mode\")\n                                _log(\"We will be COPYING found files and confirm they are preset in %s before removing it from %s\" % (dest, last))\n\n                                # loop over the old dir files\n                                entries = process_dir(last, [])\n                                _log(\"Found %d file(s) for %s\" % ( len(entries), last))\n                                for entry in entries:\n                                    # dirs\n                                    last_dirname = os.path.dirname(entry)\n                                    dest_dirname = os.path.dirname(entry).replace(last, dest)\n\n                                    if not os.path.exists(dest_dirname):\n                                        _log(\"Creating directory %s\" % dest_dirname )\n                                        os.makedirs(dest_dirname)\n                                        dest_dirname = None\n\n                                    # files\n                                    src_file = entry\n                                    dest_file = entry.replace(last, dest)\n\n                                    if src_file == dest_file:\n                                        _log(\"something is wrong, src/dest should not match, exiting\", 2)\n                                        exit(1)\n\n                                    # check src filesize for use later\n                                    src_stat = os.path.getsize(src_file)\n                                    _log(\"%s size is %s\" % (src_file, src_stat))\n\n                                    # check disk usage\n                                    if shutil.disk_usage(dest).free <= src_stat:\n                                        _log(\"You are out of space on %s, free up space and try try again\" % dest, 2)\n                                        exit(1)\n                                    else:\n                                        _log(\"You have enough free space on %s for %s\" % (dest, dest_file))\n                                        # copy the file\n                                        _log(\"Attempting to COPY file %s to %s\" % (src_file, dest_file))\n                                        if not os.path.exists(dest_file):\n                                            shutil.copy2(src_file, dest_file)\n                                        else:\n                                            _log(\"Destination %s already exists, it will be checked against the src file in the next step\" % dest_file)\n\n                                        if os.path.exists(dest_file):\n                                            # check dest filesize for use later\n                                            dest_stat = os.path.getsize(dest_file)\n                                            _log(\"%s size is %s\" % (dest_file, dest_stat))\n\n                                            # only remove the file if the copy was successful\n                                            # this way the user will have at least one copy\n                                            if os.path.exists(dest_file) and os.path.isfile(dest_file) and src_stat == dest_stat:\n                                                _log(\"%s exists, is a file and is the correct size, so it is OK to remove the old file\" % dest_file)\n                                                os.remove(src_file)\n                                                if not os.path.exists(src_file):\n                                                    _log(\"%s removed successfully from %s\" % (src_file, last))\n                                            else:\n                                                _log(\"%s exists, but does not match %s remove it manually to replace\" % (dest, src_file), 2)\n                                        else:\n                                            _log(\"%s does not exist, can't process a file that not there\" % src_file, 1)\n\n                                    \"\"\"\n                                    # remove the empy src dir\n                                    if os.path.exists(last_dirname):\n                                        _log('Trying to remove the old dir %s' % last_dirname)\n                                        try:\n                                            os.rmdir(last_dirname)\n                                        except OSError as e:\n                                            _log(\"Directory %s isn't empty yet, going for another round\" % last_dirname)\n\n                                    if not os.path.exists(last_dirname):\n                                        _log(\"%s removed successfully\" % last_dirname)\n                                    \"\"\"\n\n                            else:\n                                _log(\"Flagged for no file-mode move: %s\" % last)\n                            _log(\"----\")\n\n                        else:\n                            _log(\"Not found: %s, do not need to migrate\" % last)\n                            pass\n                    else:\n                        pass\n\n\n    return\n\n\nif __name__ == \"__main__\":\n    parser = argparse.ArgumentParser(description='validate retronas_systems.yml')\n    parser.add_argument('--file-mode', help='move the individual files (default is dir-mode)', default=False, const=True, nargs='?', required=False)\n    args = parser.parse_args()\n    main(args)\n"
  },
  {
    "path": "scripts/maint/retronas-systems-manager.py",
    "content": "#!/usr/bin/env python3\n\nimport os\nimport argparse\nimport yaml\nimport copy\n\nretronas_systems = \"../../ansible/retronas_systems.yml\"\nignored = [\"system_links\"]\n\ndef main(args):\n\n    with open(retronas_systems, \"r\") as input:\n        data = yaml.safe_load(input)\n\n    for key in ignored:\n        data.pop(key)\n\n    if args.add_new_system or args.add_new_project:\n        if args.add_new_system and args.add_new_system not in data.keys():\n            new = copy.deepcopy(data[\"system_template\"])\n            new[0]['pretty_name'] = args.add_new_system\n            data[\"system_map\"].append(new[0])\n\n        if args.add_new_project:\n            for key in data.keys():\n                for entry in data[key]:\n                    if not args.add_new_project in entry.keys():\n                        entry[args.add_new_project] = \"\"\n\n        print(yaml.dump(data, sort_keys=False, default_flow_style=False))\n\n    elif args.check_project:\n        chk_ignored = [\"system_links\"]\n        for key in data.keys():\n            for entry in data[key]:\n                if args.check_project not in entry.keys() and key not in chk_ignored:\n                    print(f\"{args.check_project} not found in {entry['src']} ({key})\")\n        print(f\"Check for {args.check_project} completed\")\n\n    else:\n        print(\"Nothing to do, try asking for --help\")\n\n\nif __name__ == \"__main__\":\n    parser = argparse.ArgumentParser()\n    parser.add_argument('--add-new-system', type=str, help='process system, add new if not exists')\n    parser.add_argument('--add-new-project', type=str, help='add new project to all systems')\n    parser.add_argument('--check-project', type=str, help='check if project is included in all systems')\n    args = parser.parse_args()\n    main(args)\n"
  },
  {
    "path": "scripts/maint/sort_menu_json.py",
    "content": "#!/usr/bin/env python3\n\n#\n# reindex items in a menu\n# code is fairly jank but will do for now\n#\n\n\nimport os, json, argparse\n\ndef main(args):\n\n    tmp = {}\n    input_file = args.input\n\n    if os.path.exists(input_file):\n        data = None\n        with open(input_file, 'r') as f:\n            data = json.load(f)\n\n        menu = data[\"menu\"]\n        items = []\n\n        # grab items and stick them in a temp dict\n        back = None\n        for item in menu[\"items\"]:\n            if item[\"title\"] != \"Back\":\n                tmp[item[\"title\"].lower()] = item\n            else:\n                back = item\n     \n        # soft the dict\n        tmp_sorted = sorted(tmp.items() )\n        tmp_sorted.insert(0, ( \"back\", back ))\n        \n        for idx, item in enumerate(tmp_sorted):\n            entry = item[1]\n            pidx = str(idx + 1).zfill(2)\n            entry[\"index\"] = pidx\n            items.append(entry)\n\n        data[\"menu\"][\"items\"] = items\n\n        output_data = json.dumps(data)\n        print(output_data)\n\n    return\n\nif __name__ == '__main__':\n\n    parser = argparse.ArgumentParser()\n    parser.add_argument('--input', type=str, required=True)\n\n    args = parser.parse_args()\n    main(args)"
  },
  {
    "path": "scripts/maint/tests/lint-yaml.sh",
    "content": "#!/bin/bash\n\nPROFILE=${1:-min}\n\nif [ ! -x /usr/bin/ansible-lint ]\nthen\n    sudo apt-get install -y ansible-lint\nfi\n\nfor i in $(ls /opt/retronas/ansible/*.yml); do echo -n \"$i: \";ansible-lint --profile=$PROFILE $i; done\n"
  },
  {
    "path": "scripts/maint/tests/test-retronas-json.py",
    "content": "#!/usr/bin/env python3\n\n#\n#\n#\n\nimport os\nimport json\nfrom random import randint\n\nretronas_json = '../../../config/retronas.json'\n\n\ndef _log(s):\n    print(s)\n\ndef main():\n    if os.path.exists(retronas_json):\n        _log(\"[START] Starting tests\")\n        try:\n            data = None\n            with open(retronas_json, 'r') as f:\n                data = json.load(f)\n\n            if data is not None:\n                _log(\" [TEST] JSON config Version: %s\" % data['version'])\n\n                # menus titles\n                menus = []\n                for menu in data['dialog']:\n                    menus.append(menu)\n\n                # random item\n                menu = randint(0,len(data['dialog'])-1)\n                _log(\" [TEST] Random Menu: \\\"%s\\\"\" % data['dialog'][menus[menu]]['title'])\n\n            _log(\"[RESULT] PASS can process the json data\")\n\n        except json.JSONDecodeError as e:\n            _log(\"Failed to read json data: %s\" % e)\n\n    else:\n        _log(\"Failed to find %s\" % retronas_json)\n\n\nif __name__ == \"__main__\":\n    main()\n\n"
  },
  {
    "path": "scripts/patch/cache-dir.sh",
    "content": "#!/bin/sh\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\n\n[ ! -d $ACACHEDIR ] && mkdir -p $ACACHEDIR\n"
  },
  {
    "path": "scripts/patch/git-config.sh",
    "content": "#!/bin/bash\n\ntouch $HOME/.gitconfig\ngit config list --global | grep -E \"safe\\.directory=*\" &>/dev/null\nif [ $? -eq 1 ] \nthen\n  git config --global --add safe.directory '*'\nfi\n"
  },
  {
    "path": "scripts/patch/install-jq.sh",
    "content": "#!/bin/bash\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\n# jq, kept here to handle migration to new menu system\nif [ ! -f /usr/bin/jq ] \nthen\n  CHECK_PACKAGE_CACHE\n  apt-get -y install jq\n  [ $? -ne 0 ] && PAUSE && EXIT_CANCEL\nfi"
  },
  {
    "path": "scripts/patch/new-startup-file.sh",
    "content": "#!/bin/bash \n\n### Checking for new startup, old installs wont have it\nif [ ! -x /usr/local/bin/retronas ]\nthen\n    clear\n    #installing a simple starup script\n    cp /opt/retronas/dist/retronas /usr/local/bin/retronas\n    chmod a+x /usr/local/bin/retronas\n    echo -e \"We have upgraded your RetroNAS, you can now run the RetroNAS config tool with the following command:\\n\\nretronas\\n\\nThis message will appear only once\\n\"\n    echo \"Press enter to continue\"\n    read -s\nfi"
  },
  {
    "path": "scripts/patch/update-retronas_vars.sh",
    "content": "#!/bin/bash\n\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\nexport ANDCFG=${ANDIR}/retronas_vars.yml.default\n\n# check for CR on last line\nif ! tail -n1 ${ANDCFG} | egrep -l $'\\r'\\$\nthen\n  sed -i -e '$a\\' ${ANCFG}\nfi\n\nupdate_config() {\n  local CHECK=$1\n  local MATCH=$2\n  local FORCE=$3\n  local FVAL=$4\n\n  if [ -z \"${CHECK}\" ]\n  then\n    if ! awk \"/^${MATCH}/\" ${ANCFG} &>/dev/null\n    then\n      # look up defaults\n      if awk \"/^${MATCH}/\" ${ANDCFG} &>/dev/null\n      then\n        VALUE=$(awk \"/^${MATCH}:/{gsub(/\\\"/,\\\"\\\");print \\$2}\" ${ANDCFG} )\n        echo \"${MATCH}: \\\"${VALUE}\\\"\" >> ${ANCFG}\n      else\n        echo \"Failed to match on $MATCH the default config may be broken\"\n        exit 1\n      fi\n    else\n      if [ $FORCE -ne 0 ]\n      then\n        sed -i \"s/${MATCH}:.*/${MATCH}: \\\"${FVAL}\\\"/\" ${ANCFG}\n      fi\n    fi\n  fi\n}\n\nETH_DEV=$(ip -4 -br a | awk '/^e[nt]/{print $1}' | head -n1)\n[ ! -z $ETH_DEV ] && ETH_IP=$(ip -4 -br a show dev $ETH_DEV | awk '!/127/{sub(/\\/[0-9].+$/, \"\"); print $3}' | head -n1)\nWL_DEV=$(ip -4 -br a | awk '/^wl/{print $1}' | head -n1)\n[ ! -z $WL_DEV ] && WL_IP=$(ip -4 -br a show dev $WL_DEV | awk '!/127/{sub(/\\/[0-9].+$/, \"\"); print $3}' | head -n1)\n\n#########################################################################################################\n#            |CHECK                  |MATCH                                      |FORCE    |VALUE\nupdate_config \"${OLDRNGROUP}\"         \"retronas_group\"                            1        \"${OLDRNUSER}\"\n# 2024-05-31 - networking\nupdate_config \"${OLDRETROIF}\"         \"retronas_net_retro_interface\"              0\nupdate_config \"${OLDMODERNIF}\"        \"retronas_net_modern_interface\"             0\nupdate_config \"${OLDRETRODHCP}\"       \"retronas_net_retro_dhcprange\"              0\nupdate_config \"${OLDRETROROUTER}\"     \"retronas_net_retro_router\"                 0\nupdate_config \"${OLDRETRONTP}\"        \"retronas_net_retro_ntp\"                    0\nupdate_config \"${OLDRETROIP}\"         \"retronas_net_retro_ip\"                     0\nupdate_config \"${OLDRETROSUBNET}\"     \"retronas_net_retro_subnet\"                 0\nupdate_config \"${OLDRETRODNS}\"        \"retronas_net_retro_dns\"                    0\nupdate_config \"${OLDUPDNS1}\"          \"retronas_net_upstream_dns1\"                0\nupdate_config \"${OLDUPDNS2}\"          \"retronas_net_upstream_dns2\"                0\nupdate_config \"${OLDWIFIINT}\"         \"retronas_net_wifi_interface\"               0\nupdate_config \"${OLDWIFISSID}\"        \"retronas_net_wifi_ssid\"                    0\nupdate_config \"${OLDWIFICHANNEL}\"     \"retronas_net_wifi_channel\"                 0\nupdate_config \"${OLDWIFIHWMODE}\"      \"retronas_net_wifi_hwmode\"                  0\nupdate_config \"${OLDWIFICC}\"          \"retronas_net_wifi_countrycode\"             0\nupdate_config \"${OLDWIFIIP}\"          \"retronas_net_wifi_ip\"                      0\nupdate_config \"${OLDWIFISUBNET}\"      \"retronas_net_wifi_subnet\"                  0\nupdate_config \"${OLDWIFIDHCP}\"        \"retronas_net_wifi_dhcprange\"               0\nupdate_config \"${OLDWIFIROUTER}\"      \"retronas_net_wifi_router\"                  0\nupdate_config \"${OLDWIFINTP}\"         \"retronas_net_wifi_ntp\"                     0\nupdate_config \"${OLDWIFIDNS}\"         \"retronas_net_wifi_dns\"                     0\n# 2024-06-10\nupdate_config \"${OLDW3DSQRIP}\"        \"retronas_net_3dsqr_interface\"              0\n"
  },
  {
    "path": "scripts/static/check-samba-user.sh",
    "content": "#!/bin/bash\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\nUSERNAME=${OLDRNUSER}\n\n# hack to check if the user exists\nsmbpasswd -e $USERNAME &> /dev/null\nif [ $? -eq 1 ]\nthen\n        # run the smbpasswd config\n        cd /opt/retronas/dialog/\n        bash retronas_password.sh\nfi\n"
  },
  {
    "path": "scripts/static/clean-broken-symlinks.sh",
    "content": "#!/bin/bash\n\nset -ue\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\necho \"Running Checks\"\n[ $OLDRNPATH == \"/\" ] && exit 1\n[ ! -d $OLDRNPATH ] && echo \"Path not found: $OLDRNPATH\" && exit 1\n\necho \"Starting scan for path: $OLDRNPATH\"\nfind \"$OLDRNPATH\" -xtype l -exec rm \"{}\" \\;\n\necho \"Done\"\nPAUSE"
  },
  {
    "path": "scripts/static/get-drives.sh",
    "content": "#!/bin/bash\n\n# Get Available drives\nSELECT_DRIVE() {\n  \n  PS3=\"Please select a drive ($1): \"\n  DRIVES=(\"$(grep -E \"/dev/(sd|hd)[a-z]+\" /proc/mounts | awk '{print $1}') exit\")\n  select DRIVE in \"${DRIVES[@]}\"\n  do  \n\n    [ $DRIVE == \"exit\" ] && echo \"Exiting...\" && exit 0\n    \n    read -p \"Are you sure? [y/N]: \" ANSWER\n\n    case $ANSWER in\n      y|Y)\n        [ ! -z \"$DRIVE\" ] && echo $DRIVE\n        exit\n        ;;\n      *)  \n        SELECT_DRIVE $1\n        ;;\n    esac\n  done\n}\n\nSELECT_DRIVE"
  },
  {
    "path": "scripts/static/get-interfaces.sh",
    "content": "#!/bin/bash\n\n\n"
  },
  {
    "path": "scripts/static/git-switch-branch.sh",
    "content": "#!/bin/bash\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\n\n# Get Available branches\nSELECT_BRANCH() {\n  \n    cd /opt/retronas\n\n    # remove deleted remote branches from output\n    git fetch --prune origin &> /dev/null\n\n    PS3=\"Please select a branch ($OLDRNBRANCH): \"\n    select BRANCH in $(git branch --remotes | awk -F'/' '!/HEAD/{print $2}' ) exit\n    do  \n\n    [ $BRANCH == \"exit\" ] && echo \"Exiting...\" && exit 0\n\n    read -p \"Are you sure? [y/N]: \" ANSWER\n\n    case $ANSWER in\n        y|Y)\n        if [ ! -z \"$BRANCH\" ]\n        then\n            git clean -d -f\n            git reset --hard\n            git checkout $BRANCH 1>/dev/null\n            echo \"Changed branched to $BRANCH, please restart RetroNAS (press CTRL-C key combination)\"\n            PAUSE\n        fi\n        exit\n        ;;\n        *)  \n        SELECT_BRANCH $OLDRNBRANCH\n        ;;\n    esac\n    done\n}\n\nSELECT_BRANCH\n"
  },
  {
    "path": "scripts/static/network-example-presets-standalone.sh",
    "content": "#!/bin/bash\n#\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\ncat << EOD\n# standalone option\n#\n# retro network is available on both ethernet and wifi intefaces\n#\n# a wifi access point is available to connect retro devices\n#\n# internet access must be provided through an additional (temporary interface)\n#\n\n                             |\n                             |\n               ingress       |       ingress\n              +--------------+--------------+\n              |              |              |\n    ethernet  | retro              retro-ap |  wifi \n     (eth0)   | 10.99.1             10.99.2 | (wlan0)\n              |              |              |\n              +--------------+--------------+\n                             | ssid: retronas\n                             | pass:   auto\n                             |\nEOD\n\nPAUSE"
  },
  {
    "path": "scripts/static/network-example-presets-zoned.sh",
    "content": "#!/bin/bash\n#\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\ncat << EOD\n# zoned option\n#\n# in this mode the the retro network is available on the ethernet interface\n# of the pi while access to the modern network in avalable on the wifi interface.\n#\n# the retro network is able to access services in its own domain. access to the\n# main network is provided _out_ of the wifi interface and subsequently the\n# internet\n# \n# firewalld is used to provide isolation between the networks, traffic is\n# permitted out of the wifi interface. Incoming traffic is permitted through\n# firewall rulesets\n\n                             |\n                             |\n               ingress       |        egress\n              +--------------+--------------+\n              |              |              |\n    ethernet  | retro     ---+-->   modern  |  wifi \n     (eth0)   | 10.99.x      |              | (wlan0)\n              |              |              |\n              +--+---+---+---+---+---+---+--+\n                 |   |   |   |   |   |   |\n  p/service      |   |   |   |   |   |   |   ssh (22/tcp)\n  >--------------+   |   |   |   |   |   +--------------<\n  samba              |   |   |   |   |    samba (445/tcp)\n  >------------------+   |   |   |   +------------------<\n  dhcp|(m)dns|icmp|ntp   |   |   |     cockpit (9090/tcp)\n  >----------------------+   |   +----------------------<\nEOD\n\nPAUSE"
  },
  {
    "path": "scripts/static/permissions.sh",
    "content": "#!/bin/bash\n#\n# This should mimic the old dialog and let the user select a folder\n#\n#set -x\nset -u\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\nPROCSUB=${1:-all}\n\ncase $PROCSUB in\n  all)\n    PROCDIR=\"${OLDRNPATH}\"\n    ;;\n  *)\n    PROCDIR=\"${OLDRNPATH}/${PROCSUB}\"\n    ;;\nesac\n\necho \"Processing: ${PROCDIR}\"\n\nsudo chown -Rc ${OLDRNUSER}:${OLDRNGROUP} \"${PROCDIR}\"\nsudo chmod -Rc a-st,u+rwX,g+rwX,o+rX \"${PROCDIR}\"\n\necho \"Done, if there was anything to do output will be listed above\"\n"
  },
  {
    "path": "scripts/static/run-local-module.sh",
    "content": "#!/bin/bash\n\n#\n# allow users to run a playbook from their retronas local config dir\n# this will allow for custom managed options for users atop the available\n# modules in retronas \n#\n\nset -u\n\nRETRONAS_PATH=\"$(awk -F '\"' '/retronas_path/{print $2}' ../../ansible/retronas_vars.yml)\"\n[ -z $RETRONAS_PATH ] && exit 1\nMODULE_PATH=\"${RETRONAS_PATH}/config/modules\"\n[ ! -d $MODULE_PATH ] && exit 1\n\ncd $RETRONAS_PATH/config/modules\n\nif  [ -f $RETRONAS_PATH/config/modules/main.yml ]\nthen\n  ansible-playbook ./main.yml\nfi"
  },
  {
    "path": "scripts/static/service-samba.sh",
    "content": "#!/bin/bash\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\nRN_SYSTEMD_STATUS \"smbd\"\nRN_DIRECT_STATUS \"smbstatus\" \"-vv\""
  },
  {
    "path": "scripts/static/set-etherdfs-nic.sh",
    "content": "#!/bin/bash\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\nNEWINT=${1}\n\nif [ ! -z \"${NEWINT}\" ]\nthen\n    # Delete the old value\n    sudo sed -i '/retronas_etherdfs_interface:/d' \"${ANCFG}\"\n    # Add the new value and re-source\n    echo \"retronas_etherdfs_interface: \\\"${NEWINT}\\\"\" | sudo tee -a \"${ANCFG}\"\nelse\n    echo \"int can't be null\"\n    exit 1\nfi\n"
  },
  {
    "path": "scripts/static/set-top-level-dir.sh",
    "content": "#!/bin/bash -x\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\nNEWRNPATH=\"${1}\"\n\n[ ! -d \"${NEWRNPATH}\" ] && echo \"Directory does not exist\" && exit 1\n\n# Delete the old value\nsudo sed -i '/retronas_path:/d' \"${ANCFG}\"\n# Add the new value and re-source\necho \"retronas_path: \\\"${NEWRNPATH}\\\"\" | sudo tee -a \"${ANCFG}\""
  },
  {
    "path": "scripts/static/update-group.sh",
    "content": "#!/bin/bash\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\nCHECK_GROUP_EXISTS() {\n    local NEWRNGROUP=$1\n    local OLDRNGROUP=$2\n\n}\n\n\nUPDATE_GROUP() {\n\n}\n\n\nCHECK_GROUP $NEWRNGROUP $OLDRNGROUP"
  },
  {
    "path": "scripts/static/update-passwd.sh",
    "content": "#!/bin/bash\n\nset -u\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\n\nUSERNAME=${OLDRNUSER}\nPASSWD=\"${1}\"\n\n[ -z \"$PASSWD\" ] && echo \"No password supplied, exiting\" && PAUSE && EXIT_CANCEL\n\n# SYSTEM\necho \"Updating system password for $USERNAME\"\necho -e \"${PASSWD}\\n${PASSWD}\" | sudo passwd $USERNAME 2>/dev/null\n\n# SAMBA\nSMB_SYSTEMD=$(systemctl show smbd.service --full --property FragmentPath --value)\nif [ ! -z \"${SMB_SYSTEMD}\" ] && [ -f \"${SMB_SYSTEMD}\" ]\nthen\n    echo \"Updating Samba password for $USERNAME\"\n    echo -e \"${PASSWD}\\n${PASSWD}\" | sudo smbpasswd -s -a $USERNAME 2>/dev/null\nfi\n\n# ATALK\nATALK_SYSTEMD=$(systemctl show atalkd.service --full --property FragmentPath --value)\nif [ ! -z \"${ATALK_SYSTEMD}\" ] && [ -f \"${ATALK_SYSTEMD}\" ]\nthen\n    ATALKDIR=/opt/retronas/bin/netatalk2x\n    echo \"Updating AppleTalk password for $USERNAME\"\n    if [ -f ${ATALKDIR}/etc/netatalk/afppasswd ]\n    then\n      touch ${ATALKDIR}/etc/netatalk/afppasswd\n      sudo ${ATALKDIR}/bin/afpexpect.sh -a \"${USERNAME}\" \"${PASSWD}\" 2>/dev/null\n    else\n      echo \"Appears you are using Netatalk 4 or above, password not managed here\"\n    fi\nfi\n\n# X11VNC\nX11VNC=$(which x11vnc)\nif [ ! -z \"${X11VNC}\" ]\nthen\n    echo \"Updating X11VNC password for $USERNAME\"\n    sudo $X11VNC -storepasswd \"${PASSWD}\" /etc/vncpasswd_retronas\n    sudo -u $USERNAME $X11VNC -storepasswd \"${PASSWD}\" /home/$USERNAME/vncpasswd_retronas\nfi\n\n# RASCSI\nif [ -f /opt/retronas/bin/RASCSI/rascsi ]\nthen \n    echo \"Updating RASCSI password for $USERNAME\"\n    RASCSI_PASSWD=/etc/rascsi_passwd\n    touch ${RASCSI_PASSWD}\n    chmod 600 ${RASCSI_PASSWD}\n    echo -e \"${PASSWD}\" | sudo tee ${RASCSI_PASSWD}\nfi\n\n# HTPASSWD (MD5 Apache)\nHTPASSWD=/etc/retronas.htpasswd\nif [ -f $HTPASSWD ]\nthen\n  echo \"Updating htpasswd password for $USERNAME\"\n  sed -i \"/^${USERNAME}:.*/d\" $HTPASSWD\n  echo -e \"${USERNAME}:$(openssl passwd -apr1 $PASSWD)\" >> $HTPASSWD\nfi\n"
  },
  {
    "path": "scripts/static/update-retronas.sh",
    "content": "#!/bin/bash\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\n\ncd ${RNDIR}\n\ngit config pull.rebase false\ngit reset --hard HEAD\ngit pull"
  },
  {
    "path": "scripts/static/update-system-history.sh",
    "content": "#!/bin/bash\n\nset -u\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\nsource /etc/os-release\n\nCHECK_ROOT\n\ncase $ID in\n    debian|ubuntu)\n        more /var/log/apt/history.log\n        PAUSE\n        ;;\n    *)\n        echo \"Unsupported OS type $ID\"\n        PAUSE\n        EXIT_CANCEL\n        ;;\nesac"
  },
  {
    "path": "scripts/static/update-system.sh",
    "content": "#!/bin/bash\n\nset -u\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\nsource /etc/os-release\n\nCHECK_ROOT\n\ncase $ID in\n    debian|ubuntu)\n        apt-get update\n        apt-get -y upgrade\n        apt-get -y dist-upgrade\n        apt-get -y autoremove\n        PAUSE\n        ;;\n    *)\n        echo \"Unsupported OS type $ID\"\n        PAUSE\n        EXIT_CANCEL\n        ;;\nesac"
  },
  {
    "path": "scripts/static/update-username.sh",
    "content": "#!/bin/bash\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\nCHECK_USER_EXISTS() {\n    local NEWRNUSER=$1\n    local OLDRNUSER=$2\n\n}\n\n\nUPDATE_USERNAME() {\n\n}\n\n\nCHECK_USERNAME $NEWRNUSER $OLDRNUSER"
  },
  {
    "path": "scripts/static/wifi-show-passwd.sh",
    "content": "#!/bin/bash\n\nset -u\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\n#HOSTAPD_CONF=/etc/hostapd/hostapd-retronas.conf\n#if [ -f \"${HOSTAPD_CONF}\" ]\n#then\n#        CLEAR\n#        PASS=\"$(sed -rn 's/^wpa_passphrase=(.*)/\\1/p' HOSTAPD_CONF)\"\n#        SSID=\"$(sed -rn 's/^ssid=(.*)/\\1/p' $NMWIFIAP_CONF)\"\n#        echo \"SSID: $SSID PASS:$PASS\"\n#fi\n\nNMWIFIAP_CONF=/etc/NetworkManager/system-connections/wifi-retronas.nmconnection\nif [ -f \"${NMWIFIAP_CONF}\" ]\nthen\n        CLEAR\n        nmcli device wifi show-password\nelse\n        echo \"Wireless AP is not installed\"\nfi\n\nPAUSE\n"
  },
  {
    "path": "scripts/static/wifi-update-passwd.sh",
    "content": "#!/bin/bash\n\nset -u\n\n_CONFIG=/opt/retronas/config/retronas.cfg\nsource $_CONFIG\nsource ${LIBDIR}/common.sh\n\nPASSWD=\"${1}\"\n\n[ -z \"$PASSWD\" ] && echo \"No password supplied, exiting\" && PAUSE && EXIT_CANCEL\n\n#HOSTAPD_CONF=/etc/hostapd/hostapd-retronas.conf\n#if [ -f \"${HOSTAPD_CONF}\" ]\n#then\n#        sed -ri \"/^wpa_passphrase=/d\" $HOSTAPD_CONF 2>/dev/null\n#        if sed \"wpa_passphrase=${PASSWD}\" >> $HOSTAPD_CONF # 2>/dev/null\n#        then\n#                echo \"wifi password updated successfully\"\n#        fi\n#else\n#        echo \"Config file not found, assuming hostapd is not installed\"\n#fi\n\nNMWIFIAP_CONF=/etc/NetworkManager/system-connections/wifi-retronas.conf\nif [ -f \"${NMWIFIAP_CONF}\" ]\nthen\n        if nmcli connection modify wifi-retronas wifi-sec.psk \"${PASSWD}\"\n        then\n                echo \"Password updated successfully\"\n        fi\nelse\n        echo \"Wireless AP is not installed\"\nfi"
  }
]